-
Promise对象实例的resolve函数的参数除了正常的值外,还可能是另一个promise实例,如:
var p1 = new Promise((resolve,reject) => { //... }) var p2 = new Promise((resolve, reject) => { resolve(p1) })
由于 **
p1
**和 **p2
**都是promise实例,但是p2的resolve将p1作为了参数,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。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 秒之后变为rejected
。p2
的状态在 1 秒之后改变,resolve
方法返回的是p1
。由于p2
返回的是另一个 Promise,导致p2
自己的状态无效了,由p1
的状态决定p2
的状态。所以,后面的then
语句都变成针对后者(p1
)。又过了 2 秒,p1
变为rejected
,导致触发catch
方法指定的回调函数。 -
调用
resolve
或reject
并不会终结 Promise 的参数函数的执行。new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); // 2 // 1
上面代码中,调用
resolve(1)
以后,后面的console.log(2)
还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。**注意:**有种情况除外,如下:
new Promise((resolve, reject) => { resolve(1); throw new Error('执行出错了') }).then(r => { console.log(r); }).catch(err => { console.log(err) }) // 1 // "执行出错了"不会被打印出来,因为promise中状态一旦改变,就不会再变
-
promise.prototype.then()
和promise.prototype.catch()
中返回的都是promise实例,从而保证了promise的链式调用,如:new Promise((resolve, reject) => { resolve(2) }).then(res => { console.log('我被执行了') // 这时候会返回一个新的promise对象 }).then(res => { console.log('我也执行了') }) // 我被执行了 // 我也执行了
-
抛出异常的等同写法
// 写法一 const promise = new Promise(function(resolve, reject) { try { throw new Error('test'); } catch(e) { reject(e); } }); promise.catch(function(error) { console.log(error); }); // 写法二 const promise = new Promise(function(resolve, reject) { reject(new Error('test')); }); promise.catch(function(error) { console.log(error); });
-
跟传统的
try/catch
代码块不同的是,如果没有使用catch()
方法指定错误处理的回调函数,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 会吃掉错误”。const promise = new Promise(function (resolve, reject) { resolve('ok'); setTimeout(function () { throw new Error('test') }, 0) }); promise.then(function (value) { console.log(value) }); // ok // Uncaught Error: test
上面代码中,Promise 指定在下一轮“事件循环”再抛出错误。到了那个时候,Promise 的运行已经结束了,所以这个错误是在 Promise 函数体外抛出的,会冒泡到最外层,成了未捕获的错误。
-
Promisse.prototype.finally的实现方式,本质上是Promise.then的特殊实现方式,
finally
方法总是会返回原来的值。//1.等价形式 promise .finally(() => { /*语句*/ }); // 等同于 promise.then(result => {/*语句*/ return result;},error => {/*语句*/ throw error;}); //2.实现方式 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 }) ); };
-
Promise.all的特点
// 情况一: const p1 = new Promise((resolve,reject) => { setTimeout(() => { resolve(2) }, 1000) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('出错了') }, 1000) }) const promiseArr = Promise.all([p1, p2]).then(res => { console.log(res) }).catch(err => { console.log(err) }) 注:针对这种情况,只有p1和p2都fullfilled,promiseArr才会fullfilled,执行后面的then方法, 当p1和p2中有任何一个出错,都会进入catch中的方法。第一个被reject的返回值将执行catch函数,
// 情况二: const p1 = new Promise((resolve,reject) => { setTimeout(() => { resolve(2) }, 1000) }).catch(err => { console.log(err) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('出错了') }, 1000) }).catch(err => { console.log(err) }) const promiseArr = Promise.all([p1, p2]).then(res => { console.log(res) }).catch(err => { console.log(err) }) 注:针对这种情况,由于p1和p2都有自己的catch方法,p2的reject函数将由自己的catch方法捕获,catch函 数又会抛出一个新的promise实例,所以promiseArr始终执行后面的then函数,而不会执行catch方法
-
Promise.race和Promise.allsettled
(ES2020引入)
和Promise.any(ES2021引入)
的区别const a1 = Promise.race([p1, p2, p3]); //a1 是一个promise实例,当p1、p2、p3中任何一个状态发生改变,a1的状态就跟着发生改变 const a2 = Promise.allsettled([p1, p2, p3]); //a2 同样也是一个promise实例,只有当p1,p2,p3全部返回结果,a2的状态才会发生改变,a2始终是fullfilled,也就是始终执行a2.then(()=>{})回调函数,不会被reject,从而执行catch函数,p1,p2,p3是fuillfilled还是reject不对a2造成影响 const a3 = Promise.any([p1, p2, p3]); //a3 同样也是一个promise实例,跟Promise.race()相似,只要p1,p2,p3中任何一个fullfilled,a3就会fullfilled,区别在于,Promise.race()只有当p1,p2,p3全部都rejected,a3才会rejected
-
Promise.resolve和Promise.reject()
Promise.resolve和Promise.reject可以快速的生成一个resolved的Promise对象和rejected的Promise对象
Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))
-
Promise.resolve()方法的参数的几种情况
// 情况一:参数本身就是一个Promise对象,则Promise.resolve()执行后,不做任何修改,原封不动的返回这个Promise对象 var p = new Promise((resolve,reject) => { console.log(5) }) var a = Promise.resolve(p) console.log(a=== p) // true // 情况二:参数是一个thenable对象,所谓thenable对象是指具有then方法的对象,Promise.resolve执行后会将这个对象转为Promise对象,并立即执行这个对象的then方法, var thenable = { then: (resolve, reject) => resolve(2) } var p1 = Promise.resolve(thenable).then(res => { console.log(res) // 打印2 }) //情况三:参数是一个不具有then方法的的对象,或者根本不是对象,Promise.then()将返回一个新的Prmoise对象,状态为resolved,同时将resolve中的参数传递给回调函数 var p1 = new Promise('我是是一个字符串') var p = Promise.resolve(p1).then(res => { console.log(res) // 我是一个字符串 }) // 情况四:不具有任何参数,这种情况可以看出情况三的特例子,这时候直接返回一个Promise对象,状态为resolved,所以想直接生成一个Promise对象,可以直接使用Promise.resolve()快速生成是一个Promise对象
注意:立即resolve()的Promise对象,是在本轮事件循环的结束时执行,而不是在下一轮事件循环的开始时
setTimeout(() => { console.log('我也执行了') }) const p = Promise.resolve().then(() => { console.log('我先执行') }) // 我先执行 // 我也执行了
-
Promise.try()的用法(详见:[ECMAScript 6 入门](https://es6.ruanyifeng.com/)
参考:
ECMAScript 6 入门
文章主要参考阮一峰ES6教程的Promise相关知识点,对部分内容进行了总结。