Javascript 如何打破承诺链
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28803287/
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
how to break promise chain
提问by mido
I a promise in such fashion,
我以这样的方式许下诺言,
function getMode(){
var deferred = Promise.defer();
checkIf('A')
.then(function(bool){
if(bool){
deferred.resolve('A');
}else{
return checkIf('B');
}
}).then(function(bool){
if(bool){
deferred.resolve('B');
}else{
return checkIf('C');
}
}).then(function(bool){
if(bool){
deferred.resolve('C');
}else{
deferred.reject();
}
});
return deferred.promise;
}
checkIfreturns a promise, and yes checkIfcannot be modified.
checkIf返回一个 promise,并且 yescheckIf不能被修改。
How do I break out of the chain at the first match? (any way other than explicitly throwing error?)
如何在第一场比赛中跳出锁链?(除了明确抛出错误之外的任何其他方式?)
采纳答案by Bergi
I think you don't want a chain here. In a synchronous fashion, you'd have written
我想你不想要一个链条在这里。以同步方式,你会写
function getMode(){
if (checkIf('A')) {
return 'A';
} else {
if (checkIf('B')) {
return 'B';
} else {
if (checkIf('C')) {
return 'C';
} else {
throw new Error();
}
}
}
}
and this is how it should be translated to promises:
这就是它应该如何转化为承诺的方式:
function getMode(){
checkIf('A').then(function(bool) {
if (bool)
return 'A';
return checkIf('B').then(function(bool) {
if (bool)
return 'B';
return checkIf('C').then(function(bool) {
if (bool)
return 'C';
throw new Error();
});
});
});
}
There is no if else-flattening in promises.
if else承诺中没有扁平化。
回答by vilicvane
Any way other than explicitly throwing error?
除了显式抛出错误之外还有其他方法吗?
You may need to throw something, but it does not have to be an error.
你可能需要抛出一些东西,但它不一定是一个错误。
Most promise implementations have method catchaccepting the first argument as error type (but not all, and not ES6 promise), it would be helpful under this situation:
大多数 Promise 实现都有方法catch接受第一个参数作为错误类型(但不是全部,也不是 ES6 承诺),在这种情况下会很有帮助:
function BreakSignal() { }
getPromise()
.then(function () {
throw new BreakSignal();
})
.then(function () {
// Something to skip.
})
.catch(BreakSignal, function () { })
.then(function () {
// Continue with other works.
});
I add the ability to break in the recent implementation of my own promise library. And if you were using ThenFail(as you would probably not), you can write something like this:
我在我自己的 Promise 库的最近实现中添加了中断功能。如果您正在使用ThenFail(您可能不会这样做),您可以编写如下内容:
getPromise()
.then(function () {
Promise.break;
})
.then(function () {
// Something to skip.
})
.enclose()
.then(function () {
// Continue with other works.
});
回答by Martin
You can use
return { then: function() {} };
您可以使用
return { then: function() {} };
.then(function(bool){
if(bool){
deferred.resolve('A');
return { then: function() {} }; // end/break the chain
}else{
return checkIf('B');
}
})
The return statement returns a "then-able", only that the then method does nothing. When returned from a function in then(), the then() will try to get the result from the thenable. The then-able's "then" takes a callback but that will never be called in this case. So the "then()" returns, and the callback for the rest of the chain does not happen.
return 语句返回一个“then-able”,只是 then 方法什么都不做。当从 then() 中的函数返回时, then() 将尝试从 thenable 中获取结果。then-able 的“then”接受一个回调,但在这种情况下永远不会被调用。因此“then()”返回,并且不会发生链其余部分的回调。
回答by simonzack
I would just use coroutines/spawns, this leads to muchsimpler code:
我只会使用coroutines/spawns,这会导致更简单的代码:
function* getMode(){
if(yield checkIf('A'))
return 'A';
if(yield checkIf('B'))
return 'B';
if(yield checkIf('C'))
return 'C';
throw undefined; // don't actually throw or reject with non `Error`s in production
}
If you don't have generators then there's always traceur or 6to5.
如果您没有生成器,那么总会有 traceur 或 6to5。
回答by plalx
You could create a firstSucceedingfunction that would either return the value of the first succeeded operation or throw a NonSucceedingError.
您可以创建一个firstSucceeding函数,该函数将返回第一个成功操作的值或抛出NonSucceedingError.
I've used ES6 promises, but you can adapt the algorithm to support the promise interface of your choice.
我使用过 ES6 承诺,但您可以调整算法以支持您选择的承诺接口。
function checkIf(val) {
console.log('checkIf called with', val);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, [val, val === 'B']), 0);
});
}
var firstSucceeding = (function () {
return function (alternatives, succeeded) {
var failedPromise = Promise.reject(NoneSucceededError());
return (alternatives || []).reduce(function (promise, alternative) {
return promise.then(function (result) {
if (succeeded(result)) return result;
else return alternative();
}, alternative);
}, failedPromise).then(function (result) {
if (!succeeded(result)) throw NoneSucceededError();
return result;
});
}
function NoneSucceededError() {
var error = new Error('None succeeded');
error.name = 'NoneSucceededError';
return error;
}
})();
function getMode() {
return firstSucceeding([
checkIf.bind(null, 'A'),
checkIf.bind(null, 'B'),
checkIf.bind(null, 'C')
], function (result) {
return result[1] === true;
});
}
getMode().then(function (result) {
console.log('res', result);
}, function (err) { console.log('err', err); });
回答by Leonid
Try to use libs like thisone:
尝试使用这样的库:
https://www.npmjs.com/package/promise-chain-break
https://www.npmjs.com/package/promise-chain-break
db.getData()
.then(pb((data) => {
if (!data.someCheck()) {
tellSomeone();
// All other '.then' calls will be skiped
return pb.BREAK;
}
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
console.error(error);
});
回答by user2571090
i like a lot of the answers posted so far that mitigate what the q readmecalls the "pyramid of doom". for the sake of discussion, i'll add the pattern that i plunked out before searching around to see what other people are doing. i wrote a function like
我喜欢到目前为止发布的许多答案,这些答案减轻了q 自述文件所说的“末日金字塔”。为了讨论起见,我将添加我在四处寻找其他人在做什么之前发现的模式。我写了一个函数
var null_wrap = function (fn) {
return function () {
var i;
for (i = 0; i < arguments.length; i += 1) {
if (arguments[i] === null) {
return null;
}
}
return fn.apply(null, arguments);
};
};
and i did something totally analogous to @vilicvane's answer, except rather than throw new BreakSignal(), i'd written return null, and wrapped all subsequent .thencallbacks in null_wraplike
和我做了完全类似于@ vilicvane的答案,除了不throw new BreakSignal(),我会写return null,并包裹所有后续.then回调的null_wrap像
then(null_wrap(function (res) { /* do things */ }))
i think this is a good answer b/c it avoids lots of indentation and b/c the OP specifically asked for a solution that doesn't throw. that said, i may go back and use something more like what @vilicvane did b/c some library's promises might return nullto indicate something other than "break the chain", and that could be confusing.
我认为这是一个很好的答案 b/c 它避免了大量缩进,并且 b/c OP 特别要求提供一个没有缩进的解决方案throw。也就是说,我可能会回过头来使用更像 @vilicvane 所做的事情,某些图书馆的承诺可能会返回null以表明“打破链条”以外的其他内容,这可能会令人困惑。
this is more a call for more comments/answers than a "this is definitely the way to do it" answer.
这更像是呼吁更多的评论/答案,而不是“这绝对是这样做的方式”的答案。
回答by Flame_Phoenix
Probably coming late the party here, but I recently posted an answer using generators and the colibrary that would answer this question (see solution 2):
可能在这里聚会迟到了,但我最近使用生成器和co可以回答这个问题的库发布了一个答案(参见解决方案 2):
The code would be something like:
代码类似于:
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
You would pretty much write synchronous code that would be in reality asynchronous !
您几乎可以编写实际上是异步的同步代码!
Hope it helps!
希望能帮助到你!

