按顺序迭代异步函数列表,直到至少有一个返回Node.js中的真值

时间:2016-07-27 20:58:55

标签: javascript node.js promise

给定一个包含对异步函数(返回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()如何编码的概念也很好,因为我对它的实现有一些疑问。

2 个答案:

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