为什么我不能抛出一个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
包含有关错误的所有信息,包括原始堆栈,文件和行号,所以仍然可以正确报告。
重要的事情要了解这里
-
then
和catch
函数都返回新的promise对象。 -
无论是投掷还是明确拒绝,都会将当前的承诺转移到被拒绝的状态。
-
从那时
then
,catch
新的承诺对象,他们可以被链接。 -
如果您在promise处理程序(或
catch
)中抛出或拒绝,则会在下一个拒绝处理程序中处理链接path。 -
正如jfriend00提到的那样,
catch
和catch
处理程序不是同步执行的。 处理程序抛出时,会立即结束。 所以,堆栈将被解开,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); });