lix*_*ang 45 javascript asynchronous promise
有许多关于如何在使用JavaScript Promise进行编程时使用"then"和"catch"的教程.但是,所有这些教程似乎都错过了重要的一点:从then/catch块返回以打破Promise链.让我们从一些同步代码开始来说明这个问题:
try {
someFunction();
} catch (err) {
if (!(err instanceof MyCustomError))
return -1;
}
someOtherFunction();
Run Code Online (Sandbox Code Playgroud)
本质上,我正在测试一个捕获的错误,如果不是错误,我希望我将返回调用者,否则程序继续.但是,这种逻辑不适用于Promise:
Promise.resolve(someFunction).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
return -1;
}
}).then(someOtherFunction);
Run Code Online (Sandbox Code Playgroud)
这个逻辑用于我的一些单元测试,我希望函数以某种方式失败.即使我将catch更改为then块,我仍然无法打破一系列链接的Promises,因为从then/catch块返回的任何内容都将成为沿着链传播的Promise.
我想知道Promise是否能够实现这个逻辑; 如果没有,为什么?对我来说,Promise链永远不会被破坏是很奇怪的.谢谢!
编辑于2015年8月16日:根据到目前为止给出的答案,由then块返回的被拒绝的Promise将通过Promise链传播并跳过所有后续块,直到被捕获(处理).这种行为很好理解,因为它只是模仿以下同步代码(方法1):
try {
Function1();
Function2();
Function3();
Function4();
} catch (err) {
// Assuming this err is thrown in Function1; Function2, Function3 and Function4 will not be executed
console.log(err);
}
Run Code Online (Sandbox Code Playgroud)
但是,我要问的是同步代码中的以下场景(方法2):
try {
Function1();
} catch(err) {
console.log(err); // Function1's error
return -1; // return immediately
}
try {
Function2();
} catch(err) {
console.log(err);
}
try {
Function3();
} catch(err) {
console.log(err);
}
try {
Function4();
} catch(err) {
console.log(err);
}
Run Code Online (Sandbox Code Playgroud)
我想以不同的方式处理在不同函数中引发的错误.我可能会捕获一个catch块中的所有错误,如方法1中所示.但是这样我必须在catch块中创建一个大的switch语句来区分不同的错误; 而且,如果不同函数抛出的错误没有共同的可切换属性,我将无法使用switch语句; 在这种情况下,我必须为每个函数调用使用单独的try/catch块.方法2有时是唯一的选择.Promise不支持使用then/catch语句支持这种方法吗?
Roa*_*888 65
使用该语言的功能无法实现这一点.但是,可以使用基于模式的解决方案.
这是两个解决方案.
Rethrow上一个错误
这种模式基本上是合理的......
Promise.resolve()
.then(Function1).catch(errorHandler1)
.then(Function2).catch(errorHandler2)
.then(Function3).catch(errorHandler3)
.then(Function4).catch(errorHandler4)
.catch(finalErrorHandler);
Run Code Online (Sandbox Code Playgroud)
Promise.resolve()并非严格必要,但允许所有.then().catch()线条具有相同的图案,整个表达方式更容易在眼睛上.
......但是:
除非编写错误处理程序以便它们能够区分先前抛出的错误和新抛出的错误,否则不会发生期望的链跳出.例如 :
function errorHandler1(error) {
if (error instanceof MyCustomError) { // <<<<<<< test for previously thrown error
throw error;
} else {
// do errorHandler1 stuff then
// return a result or
// throw new MyCustomError() or
// throw new Error(), new RangeError() etc. or some other type of custom error.
}
}
Run Code Online (Sandbox Code Playgroud)
现在:
if(error instanceof MyCustomError)协议的错误处理程序捕获(例如最终的.catch()).如果您需要灵活地跳转到链的末尾,这种模式将非常有用,具体取决于抛出的错误类型.我期待的情况很少见.
绝缘捕获
另一种解决方案是引入一种机制来保持每个.catch(errorHandlerN)"绝缘",使得它仅捕获由其相应的错误而FunctionN不是任何先前的错误引起的错误.
这可以通过在主链中仅具有成功处理程序来实现,每个成功处理程序包括包含子链的匿名函数.
Promise.resolve()
.then(function() { return Function1().catch(errorHandler1); })
.then(function() { return Function2().catch(errorHandler2); })
.then(function() { return Function3().catch(errorHandler3); })
.then(function() { return Function4().catch(errorHandler4); })
.catch(finalErrorHandler);
Run Code Online (Sandbox Code Playgroud)
这里Promise.resolve()起着重要的作用.没有它,Function1().catch(errorHandler1)将在主链中catch()不会与主链隔离.
现在,
如果您希望始终跳到链的末尾而不管抛出的错误类型,请使用此模式.不需要自定义错误构造函数,并且不需要以特殊方式编写错误处理程序.
用例
选择哪种模式将取决于已经考虑的因素,也可能取决于项目团队的性质.
jfr*_*d00 12
首先,我看到这部分代码中的一个常见错误可能让您感到困惑.这是您的示例代码块:
Promise.resolve(someFunction()).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
return -1;
}
}).then(someOtherFunction());
Run Code Online (Sandbox Code Playgroud)
您需要传递函数引用,而不是实际调用函数并传递它们的返回结果.所以,上面的代码应该是这样的:
Promise.resolve(someFunction()).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
// returning a normal value here will take care of the rejection
// and continue subsequent processing
return -1;
}
}).then(someOtherFunction); // just pass function reference here
Run Code Online (Sandbox Code Playgroud)
请注意,我已经删除.then()了两个函数,因此您只是传递函数引用,而不是立即调用函数.这将允许promise基础结构决定是否在将来调用promise.如果你犯了这个错误,它将完全抛弃你的承诺是如何工作的,因为事情会被调用.
关于捕获拒绝的三个简单规则.
您可以在此jsFiddle中看到几个示例,其中显示了三种情况:
从拒绝处理程序返回常规值会导致()调用下一个解析处理程序(例如,正常处理继续),
抛入拒绝处理程序会导致正常的解析处理停止,并且所有解析处理程序都会被跳过,直到您到达拒绝处理程序或链的末尾.如果在解析处理程序中发现意外错误(这是我的问题),这是停止链的有效方法.
没有拒绝处理程序会导致正常的解析处理停止,并且所有解析处理程序都会被跳过,直到您到达拒绝处理程序或链的末尾.