如何打破承诺链
我以这样的方式承诺,
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_wrap
callbacknull_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的!
希望它有帮助!