Promise(期约) 状态机
JavaScript高级程序设计(第四版)
Promise(executorFunc)
Promise 的状态是私有的,只可以在Promise 的构造函数中,通过它的两个函数参数完成状态转换
Promise具有如下三个状态:
- pending (待定) 初始状态
- fulfilled (兑现)
- rejected (拒绝)
settled (落定) : Promise 状态 从 pending
变成 fulfilled
或rejected
的过程
注意: 状态一旦落定,Promise 的状态就不再改变

Promise resolve() 、reject() 作用对象判断技巧
Tips: 可以通过观察 resolve() 、reject() 定义的位置,来判断它是改变哪一个Promise的状态时。
我们创建Promise 实例时,向其传递一个执行器函数(executor),Promise 会将其内置的resolve() 、reject() 函数传入该函数中。
因此,我们可以在执行器函数中,利用resolve()
、reject()
函数 完成状态转换。
Promise的执行器函数,模拟实现如下:
js
// 创建实例时,区分Promise 的状态转换情况
new MyPromise((reolve, reject) => {
if(success){
resolve('some message send to this promise's next then method')
}
if(somethig wrong){
reject()
}
})
// Promise对函数参数的处理 类似下方
class MyPromise {
status = 'pending'
constructor(callback) {
//简单实例, 具体情况应该需做 this绑定处理
// 因此,我们传入的函参,得以使用resolve 、reject 完成状态状态转换及相应处理程序(回调)的调用
callback(this.resolve, this.reject)
}
resolve() {
this.status = 'fulfilled'
.....
}
reject() {
this.status = 'rejected'
.....
}
......
}
Promise.prototype.catch(onRejected) 的触发时机
Promise error handling in-depth
Promise.prototype.catch()方法用于给期约添加拒绝处理程序。这个方法只接收一个参数: onRejected 处理程序。
事实上,这个方法就是一个语法糖,调用它就相当于调用 Promise.prototype. then(null, onRejected)。
在执行new Promise(xxx).xxx.catch(xxx)
时,js执行栈同步执行 catch()
,将其函数参数 onRejected() 处理程序放到onRejectedCallbackList
里面。
并仅当Promise status 变为 rejected 时,将会把 onRejected 处理程序从onRejectedCallbackList
取出,并作为微任务,将其 放入事件队列等待执行。
若状态不为 rejected,则不执行。
什么会触发 catch(onRejected) 的 onRejected() 执行
常见场景
在日常生产工作中,导致onRejeted() 执行的常见情况有如下三点:
1. 调用 reject()
方法
通过 reject()
的执行,完成Promise状态落定为 rejected
,从而执行 onRejected()
2. 程序性错误(代码bug:ReferenceError ..... 等语法语句使用错误)
js
new Promise((resolve, reject) => {
a.push(1);
reject({
error: 'url missing in async task 1'
});
}).catch(err => console.log(err)); // // ReferenceError: a is not defined
3. throw errors 显式抛出错误
js
new Promise((resolve, reject) => {
throw new Error('terminate now');
console.log('执行?')
}).catch(err => console.log(err)); // // Error: terminate now
特殊场景
下列两个场景在日常生产中较少涉及,用于拓展了解。
4. resolve() 处理 状态为 rejected 的Promise(rejected nested promise)
js
// case 1:
const p3 = new Promise((resolve)=>{
resolve(Promise.reject(new Error('223123 3232')))
})
p3.catch(err=> console.log(err))
// case 2:
const p1 = new Promise(function (resolve, reject) {
// resolve(p2) 则 p2 状态由 p1 状态 决定, 状态变化后不可撤销
setTimeout(() => reject(new Error("fail")), 3000);
// setTimeout(() => resolve('success'), 1000);
});
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000);
});
p2.then((result) => console.log(result)).catch((error) => console.log(error));
// Error: fail
5. then() onResolve 回调返回状态为Rejected 的Promise
js
new Promise((resolve, reject)=>{
resolve()
}).then(()=>{
return Promise.reject('test')
}).catch(err=>{
console.log(' console', err)
})
catch() 会监控什么区域的错误?
catch() 会捕获区域内抛出的所有错误,并执行回调函数完成错误处理
js
// 监控Promise链中所有的问题, 包括 Promise() 执行器函数 、 then() onResolve 处理函数
asyncTask()
.then()
.then()
.then()
.catch() // covers promise constructor in asyncTask and 3 then
asyncTask()
.catch() // only covers promise constructor in asyncTask
.then()
.then()
.catch() // 2 then
// 监控到上一个catch之间的所有then() onResolve函数、catch() onRejected处理程序
try/catch 无法捕获 Promise中的错误
下述的例子中,拒绝期约的错误并没有抛到执行同步代码的线程里,而是通过浏览器异步消息队列来处理的。因此,try/catch 块并不能捕获该错误。
- 由于Promise 的异步执行和 内部封装时进行了错误捕获,因此只能利用Promise.catch()完成
js
try {
throw new Error('foo');
} catch(e) {
console.log(e); // Error: foo
}
try {
Promise.reject(new Error('bar')); }
catch(e) {
console.log(e);
} // Uncaught (in promise) Error: bar
- Promise内部抛出的错误,不会反应到外部,外部程序代码可以正常执行
js
// demo1:
console.log('start')
new Promise((resolve)=>{
// test 1: promise executor 处存在错误
throw new Error('throw error in Promise')
console.log('是否中断执行')
resolve('ressss')
})
console.log('end')
// demo2:
console.log('start')
new Promise((resolve)=>{
resolve('ressss')
}).then(res=>{
// test 2: then callback 处存在错误
console.log(res)
throw new Error('throw error in then')
console.log('是否中断执行')
})
console.log('end')
// demo3:
console.log('start')
new Promise((resolve,reject)=>{
reject('ressss')
}).catch(err=>{
// test 3: catch callback 处存在错误
console.log(err)
throw new Error('throw error in catch')
console.log('是否中断执行')
})
console.log('end')
Async await 的错误捕获
es6.ruanyifeng.com/#docs/async
使用 async 关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值的。
使用 await关键字可以暂停异步函数代码的执行,await 后的Promise 状态settled。
(注意,await 关键字会暂停执行异步函数后面的代码,让出 JavaScript 运行时的执行线程。这个行为与生成器函数中的 yield 关键字是一样的。)
async
函数内部抛出错误,会导致返回的 Promise 对象变为reject
状态。抛出的错误对象会被catch
方法回调函数接收到。
js
async function f() {
throw new Error('出错了');
}
f().then(
v => console.log('resolve', v),
e => console.log('reject', e)
)
//reject Error: 出错了
await
命令后面的 Promise 对象如果变为reject
状态,则reject
的参数会被catch
方法的回调函数接收到。
js
async function f() {
await Promise.reject('出错了');
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了
- Async 异步函数中,
await
会把 rejected promise 转变成了一个throw,因此,可以采用 try/catch 捕获错误
js
async function chainAnimationsAsync(elem, animations) {
let ret = null;
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
}
常见问题FAQ
- 如下场景,哪些异常会被catch
js
// CASE1--A/B发生异常,会执行C
new Promise(A).then(B).catch(C);
// CASE2--A/B无异常,A调用reject,会执行C
new Promise(A;reject();).then(B).catch(C);
// CASE3--A/B无异常,B调用reject:对于一层promise,不存在该写法;对于嵌套的promise,B的reject会触发外层promise的catch,C不会被执行
new Promise(A).then(B;reject();).catch(C);
- 如下场景,是否会被catch
js
// CASE1
try {
new Promise(A).then(B).catch(C);
} catch (e) {
// 不会进来
}
// CASE2
try {
new Promise(A).then(B);
} catch (e) {
// 不会进来
}