为什么我不能抛出一个Promise.catch处理程序?

为什么我不能在catchcallback中抛出一个Error ,让进程像在其他任何作用域一样处理错误?

如果我不做console.log(err)没有打印出来,我什么都不知道发生了什么。 这个过程刚刚结束…

例:

 function do1() { return new Promise(function(resolve, reject) { throw new Error('do1'); setTimeout(resolve, 1000) }); } function do2() { return new Promise(function(resolve, reject) { setTimeout(function() { reject(new Error('do2')); }, 1000) }); } do1().then(do2).catch(function(err) { //console.log(err.stack); // This is the only way to see the stack throw err; // This does nothing }); 

如果callback在主线程中执行,为什么Error被黑洞吞噬?

正如其他人所解释的那样,“黑洞”是因为扔进一个渔获物,以一个被拒绝的承诺持续下去,你就没有更多的渔获物,导致一个没有终止的连锁,吞下错误(坏的)!

添加一个捕获来看看发生了什么:

 do1().then(do2).catch(function(err) { //console.log(err.stack); // This is the only way to see the stack throw err; // Where does this go? }).catch(function(err) { console.log(err.stack); // It goes here! }); 

尽pipe失败的一步,链中继续捕获是有用的,但重做抛出对于在logging信息或清理步骤之类的事情之后继续失败是有用的,甚至可能改变哪个错误被抛出。

为了让这个错误在Web控制台中显示出来,就像你最初打算的那样,我使用这个技巧:

 .catch(function(err) { setTimeout(function() { throw err; }); }); 

即使行号也能存活下来,所以Web控制台中的链接直接指向发生(原始)错误的文件和行。

为什么它的作品

在一个被称为承诺履行或拒绝处理函数的函数中,任何exception都会被自动转换为拒绝你应该返回的承诺。 调用你的函数的承诺代码照顾这个。

另一方面,由setTimeout调用的函数总是从JavaScript稳定状态运行,即在JavaScript事件循环中运行一个新的循环。 那里没有任何东西被捕获,并把它交给Web控制台。 由于err包含有关错误的所有信息,包括原始堆栈,文件和行号,所以仍然可以正确报告。

重要的事情要了解这里

  1. thencatch函数都返回新的promise对象。

  2. 无论是投掷还是明确拒绝,都会将当前的承诺转移到被拒绝的状态。

  3. 从那时thencatch新的承诺对象,他们可以被链接。

  4. 如果您在promise处理程序(或catch )中抛出或拒绝,则会在下一个拒绝处理程序中处理链接path。

  5. 正如jfriend00提到的那样, catchcatch处理程序不是同步执行的。 处理程序抛出时,会立即结束。 所以,堆栈将被解开,exception将会丢失。 这就是为什么抛出exception拒绝当前的承诺。


在你的情况下,你通过抛出一个Error对象来拒绝do1 。 现在,当前的承诺将处于被拒绝状态,并且控制权将被转移到下一个处理程序,这在我们的情况中是这样的。

由于then处理程序没有拒绝处理程序,所以do2将不会被执行。 你可以用console.log来确认。 由于当前的承诺没有拒绝处理程序,它也将被拒绝,从前一个承诺的拒绝值,控制权将转移到下一个catch处理程序。

作为catch是一个拒绝处理程序,当你做console.log(err.stack); 在里面,你可以看到错误堆栈跟踪。 现在,你正在抛出一个Error对象,所以catch返回的promise也会被拒绝。

既然你没有附加任何拒绝处理程序的catch ,你不能够观察到拒绝。


你可以拆分链条,并更好地理解这一点,就像这样

 var promise = do1().then(do2); var promise1 = promise.catch(function (err) { console.log("Promise", promise); throw err; }); promise1.catch(function (err) { console.log("Promise1", promise1); }); 

你将得到的输出将是类似的

 Promise Promise { <rejected> [Error: do1] } Promise1 Promise { <rejected> [Error: do1] } 

catch处理程序1中,您将promise对象的值视为已拒绝。

同样的方法, catch处理程序1返回的promise也被拒绝,同样的promise被拒绝,我们在第二个catch处理程序中观察它。

根据规范(见3.III.d) :

d。 如果调用然后抛出exceptione,
一个。 如果resolvePromise或rejectPromise已被调用,则忽略它。
湾 否则,以e拒绝承诺。

这意味着如果你在这个函数中抛出exception,它将被捕获,你的承诺将被拒绝。 catch在这里没有意义,它只是.then(null, function() {})快捷方式.then(null, function() {})

我想你想在你的代码中logging未处理的拒绝。 大多数承诺库都会为其启动unhandledRejection 。 这里有关于它的讨论的相关要点 。

我尝试了上面详细介绍的setTimeout()方法…

 .catch(function(err) { setTimeout(function() { throw err; }); }); 

令人烦恼的是,我发现这是完全不可测的。 因为抛出了一个asynchronous错误,所以你不能把它封装在一个try/catch语句中,因为这个catch会被引发的时间错误所阻塞。

我回到刚刚使用一个完美工作的监听器,因为它是如何使用JavaScript,是高度可testing的。

 return new Promise((resolve, reject) => { reject("err"); }).catch(err => { this.emit("uncaughtException", err); /* Throw so the promise is still rejected for testing */ throw err; }); 

是承诺吞下错误,你只能用.catch抓住他们,在其他答案中详细解释。 如果你在Node.js并且想要重现正常的throw行为,打印堆栈跟踪到控制台和退出进程,你可以做

 ... throw new Error('My error message'); }) .catch(function (err) { console.error(err.stack); process.exit(0); }); 
Interesting Posts