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) {
    // 不会进来 
}
相关推荐
undefined&&懒洋洋4 分钟前
Web和UE5像素流送、通信教程
前端·ue5
大前端爱好者2 小时前
React 19 新特性详解
前端
随云6322 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6322 小时前
WebGL编程指南之进入三维世界
前端·webgl
寻找09之夏3 小时前
【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验
前端·vue.js
非著名架构师3 小时前
js混淆的方式方法
开发语言·javascript·ecmascript
多多米10054 小时前
初学Vue(2)
前端·javascript·vue.js
敏编程4 小时前
网页前端开发之Javascript入门篇(5/9):函数
开发语言·javascript
柏箱4 小时前
PHP基本语法总结
开发语言·前端·html·php
新缸中之脑4 小时前
Llama 3.2 安卓手机安装教程
前端·人工智能·算法