Promise catch 错误捕获汇总

Promise(期约) 状态机

JavaScript高级程序设计(第四版)

Promise(executorFunc)

Promise 的状态是私有的,只可以在Promise 的构造函数中,通过它的两个函数参数完成状态转换

Promise具有如下三个状态:

  • pending (待定) 初始状态
  • fulfilled (兑现)
  • rejected (拒绝)

settled (落定) : Promise 状态 从 pending变成 fulfilledrejected的过程

注意: 状态一旦落定,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))
// 出错了

www.zhihu.com/question/52...

  • 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) {
    // 不会进来 
}
相关推荐
哀木9 分钟前
随笔之 react 接入 @xterm 的踩坑记录
前端
野生的程序媛24 分钟前
重生之我在学Vue--第13天 Vue 3 单元测试实战指南
前端·javascript·vue.js·单元测试
Aphasia31130 分钟前
简单介绍清除浮动解决高度塌陷的四种方法✍🏻
前端·css
二川bro1 小时前
TypeScript接口 interface 高级用法完全解析
javascript·typescript
Captaincc1 小时前
这款堪称编程界的“自动驾驶”利器,集开发、调试、提 PR、联调、部署于一体
前端·ai 编程
我是小七呦1 小时前
万字血书!TypeScript 完全指南
前端·typescript
simple丶2 小时前
Webpack 基础配置与懒加载
前端·架构
simple丶2 小时前
领域模型 模板引擎 dashboard应用列表及配置接口实现
前端·架构
冰夏之夜影2 小时前
【css酷炫效果】纯css实现液体按钮效果
前端·css·tensorflow
2 小时前
告别手写Codable!Swift宏库ZCMacro让序列化更轻松
前端