Javascript 为什么我不能在 Promise.catch 处理程序中抛出?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30715367/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can I not throw inside a Promise.catch handler?
提问by demian85
Why can't I just throw an Errorinside the catch callback and let the process handle the error as if it were in any other scope?
为什么我不能只Error在 catch 回调中抛出一个并让进程处理错误,就好像它在任何其他范围内一样?
If I don't do console.log(err)nothing gets printed out and I know nothing about what happened. The process just ends...
如果我什么都不做,console.log(err)就会被打印出来,我对发生的事情一无所知。过程刚刚结束...
Example:
例子:
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
});
If callbacks get executed in the main thread, why the Errorgets swallowed by a black hole?
如果回调在主线程中执行,为什么Error会被黑洞吞噬?
回答by jib
As others have explained, the "black hole" is because throwing inside a .catchcontinues the chain with a rejected promise, and you have no more catches, leading to an unterminated chain, which swallows errors (bad!)
正如其他人所解释的,“黑洞”是因为在 a 中抛出.catch一个被拒绝的承诺继续链,并且你没有更多的捕获,导致一个未终止的链,它吞下了错误(糟糕!)
Add one more catch to see what's happening:
添加更多捕获以查看发生了什么:
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!
});
A catch in the middle of a chain is useful when you want the chain to proceed in spite of a failed step, but a re-throw is useful to continue failingafter doing things like logging of information or cleanup steps, perhaps even altering which error is thrown.
当您希望链在执行失败的情况下继续执行时,链中间的捕获很有用,但重新抛出对于在执行诸如记录信息或清理步骤之类的操作后继续失败很有用,甚至可能更改哪个错误被抛出。
Trick
诡计
To make the error show up as an error in the web console, as you originally intended, I use this trick:
为了使错误在 Web 控制台中显示为错误,正如您最初打算的那样,我使用了以下技巧:
.catch(function(err) { setTimeout(function() { throw err; }); });
Even the line numbers survive, so the link in web console takes me straight to the file and line where the (original) error happened.
即使行号仍然存在,因此 Web 控制台中的链接将我直接带到发生(原始)错误的文件和行。
Why it works
为什么有效
Any exception in a function called as a promise fulfillment or rejection handler gets automatically converted to a rejection of the promise you're supposed to return. The promise code that calls your function takes care of this.
被称为承诺履行或拒绝处理程序的函数中的任何异常都会自动转换为对您应该返回的承诺的拒绝。调用您的函数的承诺代码会处理这个问题。
A function called by setTimeout on the other hand, always runs from JavaScript stable state, i.e. it runs in a new cycle in the JavaScript event loop. Exceptions there aren't caught by anything, and make it to the web console. Since errholds all the information about the error, including the original stack, file and line number, it still gets reported correctly.
另一方面,由 setTimeout 调用的函数始终从 JavaScript 稳定状态运行,即它在 JavaScript 事件循环中的新循环中运行。异常没有被任何东西捕获,并将其发送到 Web 控制台。由于err保存了有关错误的所有信息,包括原始堆栈、文件和行号,因此仍会正确报告。
回答by thefourtheye
Important things to understand here
这里需要了解的重要事项
Both the
thenandcatchfunctions return new promise objects.Either throwing or explicitly rejecting, will move the current promise to the rejected state.
Since
thenandcatchreturn new promise objects, they can be chained.If you throw or reject inside a promise handler (
thenorcatch), it will be handled in the next rejection handler down the chaining path.As mentioned by jfriend00, the
thenandcatchhandlers are not executed synchronously. When a handler throws, it will come to an end immediately. So, the stack will be unwound and the exception would be lost. That is why throwing an exception rejects the current promise.
无论是
then和catch函数返回新承诺的对象。抛出或明确拒绝,会将当前承诺移动到拒绝状态。
由于
then和catch返回新的承诺对象,它们可以被链接起来。如果在 promise 处理程序(
then或catch)中抛出或拒绝,它将在链接路径下的下一个拒绝处理程序中处理。正如 jfriend00 所提到的,
then和catch处理程序不是同步执行的。当处理程序抛出时,它将立即结束。因此,堆栈将展开,异常将丢失。这就是为什么抛出异常会拒绝当前的承诺。
In your case, you are rejecting inside do1by throwing an Errorobject. Now, the current promise will be in rejected state and the control will be transferred to the next handler, which is thenin our case.
在你的情况下,你是do1通过扔一个Error物体来拒绝内部。现在,当前的承诺将处于拒绝状态,控制权将转移到下一个处理程序,then在我们的例子中。
Since the thenhandler doesn't have a rejection handler, the do2will not be executed at all. You can confirm this by using console.loginside it. Since the current promise doesn't have a rejection handler, it will also be rejected with the rejection value from the previous promise and the control will be transferred to the next handler which is catch.
由于then处理程序没有拒绝处理程序,因此do2根本不会执行。您可以通过console.log在其中使用来确认这一点。由于当前的 Promise 没有拒绝处理程序,它也会被前一个承诺的拒绝值拒绝,并且控制权将转移到下一个处理程序,即catch。
As catchis a rejection handler, when you do console.log(err.stack);inside it, you are able to see the error stack trace. Now, you are throwing an Errorobject from it so the promise returned by catchwill also be in rejected state.
与catch拒绝处理程序一样,当您console.log(err.stack);在其中执行操作时,您可以看到错误堆栈跟踪。现在,您正在Error从中抛出一个对象,因此返回的承诺catch也将处于拒绝状态。
Since you have not attached any rejection handler to the catch, you are not able to observe the rejection.
由于您没有将任何拒绝处理程序附加到catch,因此您无法观察到拒绝。
You can split the chain and understand this better, like this
您可以拆分链条并更好地理解这一点,就像这样
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);
});
The output you will get will be something like
您将获得的输出将类似于
Promise Promise { <rejected> [Error: do1] }
Promise1 Promise { <rejected> [Error: do1] }
Inside the catchhandler 1, you are getting the value of promiseobject as rejected.
在catch处理程序 1 中,您将获得promise对象的值被拒绝。
Same way, the promise returned by the catchhandler 1, is also rejected with the same error with which the promisewas rejected and we are observing it in the second catchhandler.
同样,catch处理程序 1返回的承诺也被拒绝,并出现与promise被拒绝相同的错误,我们正在第二个catch处理程序中观察它。
回答by RiggerTheGeek
I tried the setTimeout()method detailed above...
我尝试了setTimeout()上面详述的方法...
.catch(function(err) { setTimeout(function() { throw err; }); });
Annoyingly, I found this to be completely untestable. Because it's throwing an asynchronous error, you can't wrap it inside a try/catchstatement, because the catchwill have stopped listening by the time error is thrown.
恼人的是,我发现这是完全无法测试的。因为它抛出一个异步错误,所以你不能把它包装在一个try/catch语句中,因为在catch抛出错误时它将停止监听。
I reverted to just using a listener which worked perfectly and, because it's how JavaScript is meant to be used, was highly testable.
我恢复到只使用一个完美的监听器,因为它是 JavaScript 的使用方式,所以是高度可测试的。
return new Promise((resolve, reject) => {
reject("err");
}).catch(err => {
this.emit("uncaughtException", err);
/* Throw so the promise is still rejected for testing */
throw err;
});
回答by just-boris
According the spec (see 3.III.d):
d. If calling then throws an exception e,
a. If resolvePromise or rejectPromise have been called, ignore it.
b. Otherwise, reject promise with e as the reason.
d. 如果调用则抛出异常 e,
a。如果调用了 resolvePromise 或 rejectPromise ,则忽略它。
湾 否则,以 e 为理由拒绝 promise。
That means that if you throw exception in thenfunction, it will be caught and your promise will be rejected. catchdon't make a sense here, it is just shortcut to .then(null, function() {})
这意味着如果您在then函数中抛出异常,它将被捕获并且您的承诺将被拒绝。catch在这里没有意义,这只是捷径.then(null, function() {})
I guess you want to log unhandled rejections in your code. Most promises libraries fires a unhandledRejectionfor it. Here is relevant gistwith discussion about it.
我猜您想在代码中记录未处理的拒绝。大多数承诺库会unhandledRejection为它触发一个。这是有关它的讨论的相关要点。
回答by nuudles
I know this is a bit late, but I came across this thread, and none of the solutions were easy to implement for me, so I came up with my own:
我知道这有点晚了,但是我遇到了这个线程,并且没有一个解决方案对我来说很容易实现,所以我想出了自己的解决方案:
I added a little helper function which returns a promise, like so:
我添加了一个返回承诺的小辅助函数,如下所示:
function throw_promise_error (error) {
return new Promise(function (resolve, reject){
reject(error)
})
}
Then, if I have a specific place in any of my promise chain where I want to throw an error (and reject the promise), I simply return from the above function with my constructed error, like so:
然后,如果我在我的任何承诺链中有一个特定的地方我想抛出错误(并拒绝承诺),我只需从上面的函数返回我构造的错误,如下所示:
}).then(function (input) {
if (input === null) {
let err = {code: 400, reason: 'input provided is null'}
return throw_promise_error(err)
} else {
return noterrorpromise...
}
}).then(...).catch(function (error) {
res.status(error.code).send(error.reason);
})
This way I am in control of throwing extra errors from inside the promise-chain. If you want to also handle 'normal' promise errors, you would expand your catch to treat the 'self-thrown' errors separately.
这样我就可以控制从承诺链内部抛出额外的错误。如果您还想处理“正常”承诺错误,您可以扩展您的捕获以分别处理“自抛出”错误。
Hope this helps, it is my first stackoverflow answer!
希望这会有所帮助,这是我的第一个 stackoverflow 答案!
回答by Jesús Carrera
Yes promises swallow errors, and you can only catch them with .catch, as explained more in detail in other answers. If you are in Node.js and want to reproduce the normal throwbehaviour, printing stack trace to console and exit process, you can do
Yes 承诺会吞下错误,你只能用 来捕捉它们.catch,如其他答案中更详细的解释。如果您在 Node.js 中并想要重现正常throw行为,将堆栈跟踪打印到控制台并退出进程,您可以这样做
...
throw new Error('My error message');
})
.catch(function (err) {
console.error(err.stack);
process.exit(0);
});

