让我们谈谈控制结构。
在 JavaScript 中,调用函数时代码以两种方式流动。
- It can
return
给调用者一个值,表明它已成功完成。
- It can
throw
向调用者发出错误,表明发生了异常操作。
它看起来像:
function doSomething(){ // every function ever
if(somethingBad) throw new Error("Error operating");
return value; // successful completion.
}
try{
doSomething();
console.log("Success");
} catch (e){
console.log("Boo");
}
Promise 模拟了完全相同的行为。
在 Promises 中,当您在 Promises 中调用函数时,代码以两种方式流动:.then
处理程序:
- It can
return
指示其成功完成的承诺或值。
- It can
throw
指示发生异常状态的错误。
它看起来像:
var doSomething = Promise.method(function(){
if(somethingBad) throw new Error("Error operating");
return someEventualValue(); // a direct value works here too
}); // See note, in Q you'd return Q.reject()
Promise.try(function(){ // in Q that's Q().then
doSomething();
console.log("Success");
}).catch(function(e){
console.log("Boo");
});
Promise 模型控制流本身
承诺是概念的抽象排序操作本身。它描述了控制如何从一个语句传递到另一个语句。你可以考虑.then
对分号的抽象。
我们来谈谈同步代码
让我们看看同步代码在您的情况下会是什么样子。
function moduleA_exportedFunction() {
var serviceResults = someSynchronousFunction();
if (serviceResults.areGood) {
// We can continue with the rest of our code
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the chain
}
}
那么,继续我们的其余代码就很简单了returning
。这在同步代码和带有 Promise 的异步代码中是相同的。执行非常具体的错误处理也是可以的。
我们如何跳过同步版本中的其余代码?
doA();
doB();
doC(); // make doD never execute and not throw an exception
doD();
好吧,即使不是立即,也有is一种相当简单的方法,通过使 doC 进入无限循环来使 doD 永远不会执行:
function doC() {
if (!results.areGood) {
while(true){} // an infinite loop is the synchronous analogy of not continuing
// a promise chain.
}
}
So, it is可能永远不会解决承诺 - 就像其他答案所暗示的那样 - 返回一个待处理的承诺。然而,这是非常糟糕的流量控制,因为意图很难传达给消费者,并且可能很难调试。想象一下以下 API:
moduleA_exportedFunction- 此函数发出 API 请求并将服务作为ServiceData
如果数据可用,则对象。否则,它让程序进入死循环.
有点令人困惑,不是吗:)?然而,它确实存在于某些地方。在非常旧的 API 中找到以下内容并不罕见。
some_bad_c_api()- 这个函数 foos a bar,失败时它终止进程.
那么,到底是什么让我们对终止该 API 中的进程感到困扰呢?
这一切都与责任有关。
- 被调用的 API 负责传达 API 请求是否成功。
- 呼叫者有责任决定在每种情况下做什么。
就你而言。 ModelA根本就违反了其责任限度,不应该entitled就程序的流程做出此类决定。无论谁消费它都应该做出这些决定。
Throw
更好的解决方案是抛出错误并让消费者处理它。我会用蓝鸟承诺因为它们不仅速度快了两个数量级,而且拥有更现代的 API——它们还拥有更多much更好的调试工具 - 在这种情况下 - 用于条件捕获和更好的堆栈跟踪的糖:
moduleA_exportedFunction().then(function(result){
// this will only be reached if no error occured
return someOtherApiCall();
}).then(function(result2){
// this will be called if the above function returned a value that is not a
// rejected promise, you can keep processing here
}).catch(ApiError,function(e){
// an error that is instanceof ApiError will reach here, you can handler all API
// errors from the above `then`s in here. Subclass errors
}).catch(NetworkError,function(e){
// here, let's handle network errors and not `ApiError`s, since we want to handle
// those differently
}).then(function(){
// here we recovered, code that went into an ApiError or NetworkError (assuming
// those catch handlers did not throw) will reach this point.
// Other errors will _still_ not run, we recovered successfully
}).then(function(){
throw new Error(); // unless we explicitly add a `.catch` with no type or with
// an `Error` type, no code in this chain will run anyway.
});
因此,在一行中,您将执行在同步代码中执行的操作,就像 Promise 的情况一样。
Note Promise.method is just a convenience function Bluebird has for wrapping functions, I just hate synchronous throwing in promise returning APIs as it creates major breakage.