Promise使用笔记
PS:本文是对参考资料1的阅读笔记,可以稍微浏览下,建议还是看参考资料1。
constructor()
-
Promise在构造函数传入的函数里(通过resolve或reject)决定状态,在then()函数里注册确定状态后的回调。
-
如果在resolve里传入一个新的promise,那么当前promise的状态就不由resolve的调用决定,而是由传入的promise的状态决定。
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
-
resolve和reject不会终结Promise()的参数函数的执行。
new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); // 2 // 1
then()
-
then()的作用是为Promise实例添加状态改变时的回调函数。
-
then()返回的是一个新的Promise实例(不是原来那个Promise实例),因此可以采用链式写法。
getJSON("/posts.json") // 第一个回调函数完成以后,会将返回结果作为参数传入第二个回调函数。 .then(function(json) { return json.post; }) .then(function(post) { // ... });
-
多次调用then()形成了一棵“promise树”,树上的promise是以广度优先遍历的顺序来调用的。
const p1 = new Promise((resolve, reject)=>{console.log(1);resolve()}) const p11 = p1.then(()=>{console.log(11)}) const p111 = p11.then(()=>{console.log(111)}) const p112 = p11.then(()=>{console.log(112)}) const p12 = p1.then(()=>{console.log(12)}) const p121 = p12.then(()=>{console.log(121)}) const p122 = p12.then(()=>{console.log(122)})
输出:
1 11 12 111 112 121 122
catch()
-
catch()是then(null, onRejected)或then(undefined, onRejected)的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error); });
-
catch()不能像then()一样链式调用,但可以在一个promise上使用多次catch()注册多个onRejected()。
const p1 = new Promise((resolve, reject)=>{console.log(1);reject()}) const p11 = p1.catch(()=>{console.log(11)}) const p111 = p11.catch(()=>{console.log(111)}) const p112 = p11.catch(()=>{console.log(112)}) const p12 = p1.catch(()=>{console.log(12)}) const p121 = p12.catch(()=>{console.log(121)}) const p122 = p12.catch(()=>{console.log(122)})
输出:
1 11 12
-
catch()的链式调用得在catch()中手动抛出异常。
new Promise((resolve, reject) => { console.log(1); throw new Error('error1') }) .catch((err) => { console.log(2); throw err; }) .catch((err) => { console.log(3); })
输出:
1 2 3
-
如果Promise状态已经变成了resolved,再抛出错误是无效的:
const promise = new Promise(function(resolve, reject) { resolve('ok'); throw new Error('test'); }); promise .then(function(value) { console.log(value) }) .catch(function(error) { console.log(error) }); // ok
-
Promise内部产生的异常不会被抛到外部(这意味着外部的代码可以正常运行,并且外部的try catch捕获不到Promise内部的异常),会终止Promise继续运行,并在内部直接输出异常信息。
try { new Promise((resolve, reject)=>{ reject(new Error('test')) }) .then(()=>{ console.log('then') // 执行不到该语句 }) } catch (e) { console.log('try catch is executed') // 执行不到该语句 } setTimeout(() => { console.log('out scripts is running') }, 2000);
输出:
(node:14976) UnhandledPromiseRejectionWarning: Error: test ... (node:14976) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2) (node:14976) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. out scripts is running
在最后一个catch()中抛出的异常统一不会被抛到外部。
try { new Promise((resolve, reject) => { reject(new Error('test')) }) .then(() => { console.log('then') // 执行不到该语句 }) .catch((err) => { console.log('catch'); throw err }) } catch (e) { console.log('try catch is executed') // 执行不到该语句 } setTimeout(() => { console.log('out scripts is running') }, 2000);
输出:
catch (node:8524) UnhandledPromiseRejectionWarning: Error: test ... (node:8524) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2) (node:8524) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. out scripts is running
-
下面代码中,Promise 指定在下一轮“事件循环”再抛出错误。到了那个时候,Promise 的运行已经结束了,所以这个错误是在 Promise 函数体外抛出的,会冒泡到最外层,成了未捕获的错误。因为是在下一个事件循环抛出错误,因此当前的try catch也无法捕获。
try { const promise = new Promise(function (resolve, reject) { setTimeout(function () { throw new Error('test') }, 0) }); } catch (err) { console.log('catch'); }
输出:
Error: test
finally()
finally()是不管状态如何,最终都会执行的操作。该操作在ES2018引入。
finally()传入的方法不接收任何参数。
finally()是then()的特例,只是then()要分别指定onResolved()回调和onRejected()回调,而finall()只需要写一次。
其实现原理如下:
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
all()
all()将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.all([p1, p2, p3]);
如果p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调;
如果p1、p2、p3中有一个状态为rejected,p的状态就会变成rejected,此时第一个被reject的实例的返回值,传递给p的回调;
-
如果我们想将成功的value和错误的reason都放到一个数组中,可以像下面这样做:
const p1 = new Promise((resolve, reject) => { resolve('hello'); }) .then(result => result) .catch(e => e); // 添加了该语句 // 事实上p2指向的是catch()返回的Promise实例,该实例指向玩catch()方法后, // 也会变成resolved,因此会调用all().then()指定的回调函数。 // 如果p2没有指定catch()方法,则会调用all().catch()指定的回调函数。 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: 报错了 ... ]
race()
all()将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.race([p1, p2, p3]);
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变Promise实例的返回值,就传递给p的回调函数。
具体可以这样使用:如果5秒内没有获得结果,就将Promise的状态变为reject:
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
allSettled()
allSettled()将多个Promise实例,包装成一个新的Promise实例。该方法由ES2020引入。
const p = Promise.allSettled([p1, p2, p3]);
只有等到p1、p2、p3都返回结果(不管是什么状态),p才会结束,且状态总是fulfilled。
any()
any()将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.any([p1, p2, p3]);
只要p1、p2、p3中有一个变成fulfilled状态,p就会变成fulfilled状态;如果p1、p2、p3都变成rejected状态,就会抛出错误(是每个Promise实例的错误组成的数组)。
any()与race()的区别是,any()不会因为某一个Promise变成rejected状态而结束。
resolve()
resolve()用于将现有对象转为Promise对象,状态为fulfilled。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
根据传入值类型(包括Promise实例、thenable对象、有值但不是前两者、无值)的不同,会有不同的行为。详情见参考资料1。
reject()
resolve()用于将现有对象转为Promise对象,状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))