promise内部抛出错误,catch不到

阮一峰的ECMAScript 6入门Promise对象第四小节提到:promise抛出一个错误,就被catch方法指定的回调函数捕获。

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});

等同于:

// 写法一
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});

请问:为什么如下的形式,catch无法捕获错误

const promise = new Promise(function(resolve, reject) {
  setTimeout(function () { throw new Error('test') }, 0)
});
promise.catch(function(error) { console.log(error) });

但如果改成如下两种形式就可以。

// 改写方法1
const promise = new Promise(function(resolve, reject) {
  setTimeout(function () { 
    try{
       throw new Error('test') 
    }catch(e){
       reject(e)
    }
 }, 0)
});
// 改写方法2
const promise = new Promise(function(resolve, reject) {
  setTimeout(function () { 
    reject(new Error('test'));
 }, 0)
});
阅读 12.3k
3 个回答
const promise = new Promise(function(resolve, reject) {
  setTimeout(function () { throw new Error('test') }, 0)
});
promise.catch(function(error) { console.log(error) });

JS 事件循环列表有宏任务与微任务之分:setTimeOut是宏任务, promise是微任务,他们有各自的执行顺序;因此这段代码的执行是:

  1. 代码执行栈进入promise 触发setTimeOut,此时setTimeOut回调函数加入宏任务队列
  2. 代码执行promise的catch方法(微任务队列)此时setTimeOut回调还没有执行
  3. 执行栈检查发现当前微任务队列执行完毕,开始执行宏任务队列
  4. 执行throw new Error('test') 此时这个异常其实是在promise外部抛出的
const promise = new Promise(function(resolve, reject) {
  setTimeout(function () { 
    try{
       throw new Error('test') 
    }catch(e){
       reject(e)
    }
 }, 0)
});

是因为你在setTimeOut主动触发了promise的reject方法,因此promise的catch将会在setTimeOut回调执行后的属于他的微任务队列中找到它然后执行,所以可以捕获错误

这个跟try...catch一样,捕获不了异步异常。异步情况必须手动调用reject
Promise的状态有三种:

pending/fulfilled/rejected

异步捕获不到的异常 以及 未手动调用resolve or reject 状态都不会走到最终态fulfilled/reject(同步状态throw Error除外,这个会直接到rejected状态) ,导致这个promise一直处于pending状态

参考:https://developer.mozilla.org...

个人感觉采纳答案说得有点复杂了,化繁为简就是下面的代码:

try {
  setTimeout(function() {
    throw new Error('error from timeout!!!');
  }, 0);
} catch (e) {
  alert( "won't work", e);
}

如上所示,计时器为异步操作,执行完毕时已经没有地方可以接收它抛出来的错误。可以尝试在定时器内try...catch。

setTimeout(() => {
  try {
    throw new Error('error from timeout');
  } catch (e) {
    console.log('error caught!!', e);
  }
}, 0)

或者利用闭包,Promise的reject来抛出错误。

new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('error from timeout'));
  }, 0);
})
.catch(e => {
  console.log('error caught!!', e);
})
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题