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) {
    // 不会进来 
}
相关推荐
Larcher1 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐13 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭26 分钟前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu1 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花1 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程