为什么本机Promise在语法错误方面就像try / catch一样?
当您需要按顺序执行大量异步操作时,Prom明显具有流量控制的价值。但是,必须为每个承诺实施自己的错误处理实施的必要性有时会使他们麻烦地工作。
请使用以下代码:
asdf
let one = new Promise((resolve, reject) => {
asdf
}).catch(err => {
console.log(err)
console.log(err.stack)
})
第一个语法错误产生典型的浏览器错误和包含18个条目的堆栈跟踪。承诺版本有4个。
所以我的问题是,在编写规范并在本地实现它时,为什么它们保留了promises的用户空间实现的try / catch功能,以便它可以用于流控制但是留下标准的错误处理? / p>
答案 0 :(得分:4)
当代码加载时,Javascript解析器将抛出实际的语法错误,并且仍然是抛出的异常。基本上,当解析器决定它无法正确解析代码时,此时没有别的事情可做。它会放弃并停止解析剩下的代码。
承诺将处理的是运行时错误(在代码执行期间发生,而不是在加载/解析期间)。
根据规范,.then()
承诺处理程序是“抛出安全”,这意味着它们将抛出的异常转换为被拒绝的承诺。这样做是因为这些处理程序总是异步的,并且异步回调中的抛出异常相当无用,因为它们不能被调用代码捕获,因为调用代码不再在堆栈上,并且调用代码只能通过回调通知。异常最终只是进入一些异步基础设施,其中没有调用代码可以拦截或处理它们。因此,他们决定将异常转换为拒绝,以便可以使用承诺的所有正常拒绝处理和错误传播。
并且,因为调用代码无法捕获异常,如果promises没有执行此操作,那么您必须在几乎每个.then()
处理程序中执行自己的try / catch以确保你发现了什么问题。因此,它节省了大量额外代码,并且可以轻松确保正确捕获所有异步异常,并将错误传播回调用代码。
简而言之,一旦你习惯了它,它就会非常有用,因为无论如何无处不在的例外,我会说设计师通过决定捕捉异常并使它们变得有用而做出了一个非常好的选择。仅供参考,您可以随时在.then()
处理程序中进行自己的尝试/捕获,并根据需要随意做任何事情。
答案 1 :(得分:1)
Promise不会捕获您在解析/编译时发生的典型语法错误。您的示例不是SyntaxError(解析器无法理解您的代码),它是一个ReferenceError(解释器/运行时无法找到您命名的标识符)。
Uncaught ReferenceError: asdf is not defined
如果文字代码中存在语法错误,则不会被捕获。这是我们应该期待的:如果它无法解析代码,它就无法知道你甚至已经创建了一个承诺。
let one = new Promise((resolve, reject) => {
foo bar baz
}).catch(err => {
// ignore error
});
Uncaught SyntaxError: Unexpected identifier
请注意,如果您使用eval
在运行时生成语法错误 (从Promise-using代码的角度来看),它将被捕获。区别不在于错误的JavaScript对象类型,区别在于编译时和运行时错误。
let one = new Promise((resolve, reject) => {
eval("foo bar baz");
}).catch(err => {
console.log("Captured error:", err);
})
Captured error: SyntaxError: Unexpected identifier
答案 2 :(得分:0)
原因是当您处理异步流程时,您有 nobody 来捕获您的错误。 之前的callstack消失了。
即便:
function simulateAsyncError () {
try {
setTimeout(function () { throw new Error("Nobody caught me"); }, 1);
console.log("I have set the future error.");
} catch (err) { console.log("I caught it!"); }
}
simulateAsyncError();
// "I have set the future error."
// Uncaught Error: "Nobody caught me"
承诺是关于包装,并处理它。