mjo*_*ble 8 javascript node.js promise q
使用https://github.com/kriskowal/q库,我想知道是否可以这样做:
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
}
});
}
// Module B
moduleA_exportedFunction()
.then(moduleB_function)
.then(moduleB_anotherFunction)
.fail(function(reason) {
// Handle the reason in a general way which is ok for module B functions
})
.done()
;
Run Code Online (Sandbox Code Playgroud)
基本上,如果服务结果不好,我想使用特定于模块A内部的逻辑来处理模块A中的故障,但仍然跳过promise链中剩余的模块B函数.
跳过模块B函数的显而易见的解决方案是从模块A抛出错误/原因.但是,我需要在模块B中处理它.理想情况下,我想在模块B中不需要任何额外的代码来执行它.那.
这可能是不可能的:)或者反对Q的一些设计原则.
在这种情况下,您会建议什么样的替代方案?
我有两种方法,但都有它们的缺点:
从模块A抛出特定错误并将特定处理代码添加到模块B:
.fail(function(reason) {
if (reason is specificError) {
performVerySpecificErrorHandling();
}
else {
// Handle the reason in a general way which is ok for module B functions
}
})
Run Code Online (Sandbox Code Playgroud)在模块A中执行自定义错误处理,然后在处理错误后,抛出假的拒绝原因.在模块B中,添加条件以忽略虚假原因:
.fail(function(reason) {
if (reason is fakeReason) {
// Skip handling
}
else {
// Handle the reason in a general way which is ok for module B functions
}
})
Run Code Online (Sandbox Code Playgroud)解决方案1需要将模块A特定代码添加到模块B.
解决方案2解决了这个问题,但整个假拒绝方法看起来非常苛刻.
你能推荐其他解决方案吗?
Ben*_*aum 16
在JavaScript中,当您调用函数时,代码以两种方式流动.
return是调用者的值,表示它已成功完成.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");
}
Run Code Online (Sandbox Code Playgroud)
Promises模拟这种完全相同的行为.
在Promises中,当您在.then处理程序中调用函数时,代码以两种方式流动:
return是一个表示成功完成的承诺或值.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");
});
Run Code Online (Sandbox Code Playgroud)
承诺是对概念排序操作本身的抽象.它描述了控件如何从一个语句传递到另一个语句.您可以考虑.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
}
}
Run Code Online (Sandbox Code Playgroud)
那么,如何继续我们的其余代码就是这么简单returning.这在同步代码和带有promise的异步代码中是相同的.执行非常具体的错误处理也没问题.
我们如何跳过同步版本中的其余代码?
doA();
doB();
doC(); // make doD never execute and not throw an exception
doD();
Run Code Online (Sandbox Code Playgroud)
好吧,就算不是马上,还有是一个相当简单的方法,使国防部从来没有引起DOC进入一个无限循环执行:
function doC() {
if (!results.areGood) {
while(true){} // an infinite loop is the synchronous analogy of not continuing
// a promise chain.
}
}
Run Code Online (Sandbox Code Playgroud)
因此,有可能永远不会解决承诺 - 就像其他答案所暗示的那样 - 返回未决承诺.然而,这是非常差的流量控制,因为意图很难传达给消费者,并且可能很难调试.想象一下以下API:
moduleA_exportedFunction - 此函数发出API请求,并
ServiceData在数据可用时将服务作为对象返回.否则,它进入程序进入无限循环.
有点混乱,不是吗:)?但是,它实际上存在于某些地方.在真正的旧API中找到以下内容并不罕见.
some_bad_c_api() - 这个函数会出现一个条形,失败时会终止进程.
那么,对于那个终止API的过程有什么困扰呢?
在你的情况下.ModelA只是违反了其责任的限制,它无权对程序的流程做出这样的决定.消费它的人应该做出这些决定.
更好的解决方案是抛出错误并让消费者处理它.我将使用蓝鸟承诺在这里,因为他们不仅是两个数量级速度更快,并有一个更现代的API -他们也有很多很多更好的调试设施-在这种情况下-糖条件渔获物和更好的堆栈跟踪:
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.
});
Run Code Online (Sandbox Code Playgroud)
所以在一行中 - 你会做你在同步代码中会做的事情,就像promises的情况一样.
注意Promise.method只是Bluebird用于包装函数的一个便利函数,我只是讨厌同步抛出promise返回API,因为它会造成重大破坏.
受到 Benjamin Gruenbaum 的评论和回答的启发 - 如果我用同步代码编写此代码,我将使moduleA_exportedFunctionreturn为shouldContinue布尔值。
因此,有了承诺,它基本上会是这样的(免责声明:这是伪代码并且未经测试)
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
return true;
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
return false;
}
});
}
// Module B
moduleA_exportedFunction()
.then(function(shouldContinue) {
if (shouldContinue) {
return moduleB_promiseReturningFunction().then(moduleB_anotherFunction);
}
})
.fail(function(reason) {
// Handle the reason in a general way which is ok for module B functions
// (And anything unhandled from module A would still get caught here)
})
.done()
;
Run Code Online (Sandbox Code Playgroud)
它确实需要模块 B 中的一些处理代码,但逻辑既不是特定于模块 A 的内部结构,也不涉及抛出和忽略假错误 - 任务完成!:)
| 归档时间: |
|
| 查看次数: |
6904 次 |
| 最近记录: |