es6的promise

Last Updated:

2022-08-12

阅读promise总结

promise三种状态,并且一旦状态改变后,就不会再变

有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。 Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

promise缺点

  1. 首先,无法取消Promise,一旦新建(new)它就会立即执行,无法中途取消。(不是有then才执行,而是new就会执行)
  2. 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

then函数吃2个参数

  1. 第一个回调函数是Promise对象的状态变为resolved时调用,
  2. 第二个回调函数是Promise对象的状态变为rejected时调用。

这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数。

getJSON("/post/1.json").then(
  post => console.log("resolved: ", post),
  err => console.log("rejected: ", err)
);

then函数返回的是一个新的 Promise实例(不是原来的那个Promise实例)

因此可以采用链式写法,即then方法后面再调用另一个then方法。

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

then指定的回调函数(即入参)是异步

then指定的回调函数,将在当前脚本所有同步任务执行完后才执行。

  1. step1, 先运行下述代码:
const delay1s = new Promise(function (resolve, reject) {
    setTimeout(() => resolve('resolved'), 1000)
})
  1. step2, 等step1代码运行后,过2s,再运行下述代码
delay1s.then((v1) => {
    console.log('then的回调,执行了,执行结果是: ' + v1)
});
console.log('此处,比then的回调先执行;因为虽然已经过了2s,即delay1s的状态,已经是resolved了,此时then能马上获知状态是resolved,但是,then的回调函数还是被异步了,所以,此段文字会在then的回调之前执行')

相当于then的回调函数func1,被setTimeout(func1, 0)(可能不是100%精准,可能会设计到this指向问题,但举这个例子,是取其异步的意思)

resolve的参数如果是另一个异步操作(即是一个promise实例)resolve(new Promise(...))

此时第一个promise实例(p1),会等待resolve入参的promise实例(p2)的状态变换而变换(即采用p2的状态)。即,p2变成resolved了,那么,p1的状态也变成resolved;p2变成rejected,那么p2也变成rejected; 举例:

const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
// Error: fail

p1是一个 Promise,3 秒之后变为rejectedp2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 处理前面三个Promise产生的错误
});

上面代码中,一共有三个 Promise 对象:一个由getJSON()产生,两个由then()产生。它们之中任何一个抛出的错误,都会被最后一个catch()捕获。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

跟传统的try/catch代码块不同的是,如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应,依然执行下面的代码。(Promise 会吃掉错误)

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing().then(function() {
  console.log('everything is great');
});

setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123

上面代码中,someAsyncThing()函数产生的 Promise 对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程、终止脚本执行,2 秒之后还是会输出123。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。 不过,Node.js 有一个unhandledRejection事件,专门监听未捕获的reject错误,上面的脚本会触发这个事件的监听函数,可以在监听函数里面抛出错误。

process.on('unhandledRejection', function (err, p) {
  throw err;
});

上面代码中,unhandledRejection事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。

注意,Node 有计划在未来废除unhandledRejection事件。如果 Promise 内部有未捕获的错误,会直接终止进程,并且进程的退出码不为 0。

const promise = new Promise(function (resolve, reject) {
    console.log('最早的:new Promise')
  resolve('第三: ok: before error test');
  setTimeout(function () { 
  throw new Error('第四,但已经是另一个环境了,所以会无法捕捉,从而下面的代码不执行test'); 
  console.log('代码不会执行到这里,被throw new Error异常掉了')
  }, 0)
});
promise.then(function (value) { console.log(value) });
console.log('第二早的')

Promise实例p1,p1.catch(...)产生的新实例的p2的状态是fullfilled(即resolved), p1的状态是rejected(假设被catch了)

注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法。

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

上面代码中,p1resolvedp2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。

如果p2没有自己的catch方法,就会调用Promise.all()catch方法。

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// Error: 报错了
const p = Promise.resolve('Hello');

p.then(function (s) {
  console.log(s)
});
setTimeout(()=> {
    console.log(`setTimeout, after Promise的异步`)
}, 0)
console.log('before resolve')

finally(es2018引入),allSettled(es2020), any(es2021引入)

Promise.prototype.finally()

Promise.allSettled

Promise.any