为何第一种异步调用无法捕获异常?代码执行顺序解析
Promise链异常捕获差异与执行顺序分析
const before = () => { const p = new Promise((resolve, reject) => { // resolve('before') console.log("ok"); reject(new Error("error!!!")); }); return p; }; const handleClick = () => { const p = new Promise((resolve, reject) => { resolve(true); }); p.then(() => { before(); }) // .then(before) // .then(async () => { // await before(); // }) .then(() => { console.log("then"); }) .catch((e) => { console.error(e); }); };
一、三种调用方式的异常捕获差异原因
第一种写法(
then(() => { before(); })):
这里then的回调只是调用了before(),但没把它返回的Promise给return出去。then方法会自动把回调的返回值包装成新Promise——因为回调没写return,默认返回undefined,所以这个新Promise是成功状态,直接走到下一个then。而before()返回的那个失败的Promise跟当前的链完全没关系,相当于“脱缰”了,它的错误自然不会被后面的catch接住,最后会变成未捕获的Promise异常。第二种写法(
then(before)):
直接把before作为then的回调函数,then方法会自动接收回调返回的Promise,并把它纳入当前的Promise链。当before返回的Promise失败时,整个链的状态就变成失败,后续的catch就能直接捕获到这个错误。第三种写法(
then(async () => { await before(); })):async函数本身就会返回一个Promise,await before()会等着before生成的Promise出结果。当before的Promise失败时,await会直接抛出这个错误,导致async函数返回的Promise也变成失败状态,这个状态会顺着Promise链传下去,最后被catch捕获。
二、三种方式的执行顺序
第一种写法的执行流程
- 调用
handleClick,创建第一个Promise,内部直接resolve(true),这个Promise立刻变成成功状态。 - 执行
p.then(() => { before(); })的回调:- 调用
before(),创建内部Promise,立刻打印ok,然后标记为失败状态。 - 但回调没返回这个Promise,所以
then生成一个成功状态的新Promise(值为undefined)。
- 调用
- 进入下一个
then,打印then。 - 整个链是成功状态,
catch不会触发。 - 最后,
before返回的失败Promise没人处理,抛出未捕获异常。
第二种写法的执行流程
- 调用
handleClick,创建第一个Promise,立刻变成成功状态。 - 执行
p.then(before),调用before():- 创建内部Promise,打印
ok,然后标记为失败状态,返回这个Promise。
- 创建内部Promise,打印
- 整个Promise链变成失败状态,跳过后面的
then,直接进入catch,打印错误信息。
第三种写法的执行流程
- 调用
handleClick,创建第一个Promise,立刻变成成功状态。 - 执行
p.then(async () => { await before(); })的回调:- 调用
before(),创建内部Promise,打印ok,标记为失败状态。 await遇到失败的Promise,抛出错误,导致async函数返回的Promise变成失败状态。
- 调用
- 整个链变成失败状态,跳过后面的
then,进入catch打印错误信息。
内容的提问来源于stack exchange,提问作者zhaolei
相关产品推荐
相关产品推荐

