给定一个包含对异步函数(返回promises或简单值)的引用的数组,我希望按顺序迭代它来解析每个元素,直到至少有一个元素解析为truthy值。
这个想法来自这个简单的同步代码:
var hasAccess = (user.isAdmin() || user.isManager() || entity.isOwnedBy(user));
上面的代码适用于同步函数,但对于异步函数会中断。我正在寻找上述代码的异步替换。
我希望它能像这样被调用(在ES6语法中为了简洁):
atLeastOneTruthy([
() => user.isAdmin(), // Called first
() => user.isManager(), // Called second if first returned falsy value
() => entity.isOwnedBy(user) // Called third if all previous returned falsy values
]).then(function (result) {
// result === (true || false)
}).catch(function () {
// Promise chain is rejected when at least one element in rejected
});
上例中的所有函数都直接返回promises或简单值。
这是否有开箱即用的解决方案?我使用Bluebird的承诺,然而,我找不到任何合适的API。我无法使用,例如Promise.any()
,因为它只检查已解决的承诺,并且不会检查返回的值。我也不能使用Promise.map()
,因为它会执行所有功能,但我不想在必要时执行它们(即前一个元素失败时)。
节点模块建议会很棒(我找不到),但是,atLeastOneTruthy()
如何编码的概念也很好,因为我对它的实现有一些疑问。
答案 0 :(得分:2)
因为then
处理程序可以返回一个promise,你可以只做一个递归解决方案。
function atLeastOneTruthy(arrayOfCallables) {
// Keep a numeric index to avoid mutating the input array.
// This implementation is not safe for item removal, but appending is OK.
let nextCallableIndex = 0;
// Start with a false value as a base case. Use a "named function expression"
// to allow for recursion.
return Promise.resolve(false).then(function innerHandler(returnValue) {
if (returnValue) {
// If truthy, return immediately. Nothing else is evaluated.
return returnValue;
} else if (nextCallableIndex < arrayOfCallables.length) {
// If falsy, get the next iterable...
let nextCallable = arrayOfCallables[nextCallableIndex++];
// ...then call it and try again, regardless of whether nextCallable
// returns a simple value or a promise.
return Promise.resolve(nextCallable()).then(innerHandler);
} else {
// We're out of callables, and the last one returned a falsy value.
// Return false. (You could also return returnValue to parallel ||.)
return false;
}
});
}
另请参阅: Named function expressions。您还可以重构使用简单的嵌套函数。
答案 1 :(得分:0)
可能你可以创建一个像Promise.any()
这样的新Promise方法,它接受一系列的promise并在其中任何一个解析时解析,只有在没有解析时才会拒绝。
var one = _ => new Promise((res,rej) => setTimeout(rej.bind(null,"boom"),1000)),
two = _ => new Promise((res,rej) => setTimeout(rej.bind(null,"crash"),1500)),
three = _ => new Promise((res,rej) => setTimeout(res.bind(null,"yay..!"),500));
Promise.prototype.constructor.any = function(proms){
return proms.length ? proms[0].then(v => v, r => Promise.any(proms.slice(1)))
: Promise.reject("none resolved");
};
Promise.any([one(),two(),three()]).then(v => console.log("From promise.any with love",v), r => console.log(r));
Promise.all([one(),two(),three()]).then(v => console.log(v), r => console.log("From Promise.all with love",r));