如何打破承诺链

我以这样的方式承诺,

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; } 

checkIf返回一个promise,并且是checkIf 不能被修改

如何在第一场比赛中突围? (除了明确抛出错误以外的任何方式?)

我想你不想在这里连锁。 以同步的方式,你会写

 function getMode(){ if (checkIf('A')) { return 'A'; } else { if (checkIf('B')) { return 'B'; } else { if (checkIf('C')) { return 'C'; } else { throw new Error(); } } } } 

这是如何转化为承诺:

 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(); }); }); }); } 

没有if else的承诺。

除了明确抛出错误以外的任何方式?

你可能需要抛出一些东西,但不一定是错误。

大多数承诺实现方法catch接受第一个参数作为错误types(但不是全部,而不是ES6的承诺),这将是有益的在这种情况下:

 function BreakSignal() { } getPromise() .then(function () { throw new BreakSignal(); }) .then(function () { // Something to skip. }) .catch(BreakSignal, function () { }) .then(function () { // Continue with other works. }); 

我增加了在最近实现我自己的承诺库的能力。 如果你正在使用ThenFail (你可能不会),你可以写这样的东西:

 getPromise() .then(function () { Promise.break; }) .then(function () { // Something to skip. }) .enclose() .then(function () { // Continue with other works. }); 

我只是使用协程/ 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 } 

如果你没有发电机,那么总是有traceur或6to5。

您可以创buildfirstSucceeding函数,该函数将返回第一个成功操作的值或抛出NonSucceedingError

我已经使用了ES6承诺,但是您可以调整algorithm以支持您所select的承诺界面。

 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); }); 

你可以使用return { then: function() {} };

 .then(function(bool){ if(bool){ deferred.resolve('A'); return { then: function() {} }; // end/break the chain }else{ return checkIf('B'); } }) 

return语句返回一个“then-able”,只是then方法什么都不做。 then()中的函数返回时,then()将尝试从thenable获取结果。 那么当时的“然后”会callback,但在这种情况下永远不会被调用。 所以“then()”返回,并且链条的其余部分的callback不会发生。

我喜欢迄今为止发布的很多答案,以减轻q readme所说的“末日金字塔”。 为了讨论起见,我会添加一些模式,然后在四处搜寻,看看其他人在做什么。 我写了一个函数

 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); }; }; 

我做了一些完全类似于@ vilicvane的答案,除了不是throw new BreakSignal() ,我写了return null ,并包装所有后续null_wrapcallbacknull_wrap

 then(null_wrap(function (res) { /* do things */ })) 

我认为这是一个很好的答案B / C它避免了大量的缩进和B / C的OP特别要求一个不会throw的解决scheme。 说,我可以回去使用更像@ vilicvane所做的事情。b / c某些图书馆的承诺可能会返回null来表示“断链”以外的内容,这可能会造成混淆。

这更多的是要求更多的评论/回答,而不是“这绝对是做到这一点”的答案。

可能会迟到这里的派对,但我最近发布了一个答案,使用生成器和co库来回答这个问题(见解决scheme2):

代码会是这样的:

 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); 

你几乎可以写同步代码,实际上是asynchronous的!

希望它有帮助!