带有谓词的JavaScript Promise.all

时间:2019-01-10 22:44:54

标签: javascript ecmascript-6 ecmascript-5

首先给出一个带有承诺列表的数组:

var promises = [promise1, promise2, promise3];

我希望能够使用谓词执行所有这些承诺。如果第一个谓词返回false,则立即返回给出截至该点已处理的结果并取消所有剩余的Promise。例如:

Promise.all(promises, p => p != false))
  .then(results => console.log(results));

结果应该具有直到第一个谓词失败为止要处理的内容。

应按顺序而非并行处理承诺。

4 个答案:

答案 0 :(得分:0)

您不能取消承诺。但是,您可以在上一个操作完成后才开始下一个操作:

 async function doUntil(generator, predicate) {
   const result = [];
   let curr;
   while(predicate(curr = await generator()))
     result.push(curr);
   return result;
}

// usable as:
const users = await doUntil(
  () => User.getOneAsync(),
  user => user.loggedIn
);

为确保可以很容易地将这些承诺应用于承诺列表,但是随后仍然执行那些不属于结果的承诺,结果就毫无用处了:

 const promises = [/*...*/];

 const result = await doUntil(
  () => promises.shift(),
  p => p
 );

答案 1 :(得分:0)

我的解决方案与Yona的一个Promise非常相似,但是从某种意义上说,您只返回了满足谓词的最后一个返回的Promise值。

在下面的代码中,我简单地构造了一个测试承诺,该承诺在500毫秒后返回一个随机数。然后将随机数传递给谓词,如果该谓词超过0.5,则返回true。

一种改进是,我们尝试捕获承诺可能失败的事件。如果promise失败,则链将立即断开,并返回最后一个结果,类似于promise返回的结果使谓词失败的情况:

// Here we simply set a test promise that returns a random number after 0.5s
function testPromise() {
  return new Promise(resolve => {
    window.setTimeout(() => resolve(Math.random()), 500);
  });
}

// Execute promises in a specified order, evaluate their output by a predicate
async function awaitSequentialPromises(promises, predicate) {
  let result;
  for (const promise of promises) {
    // Try, so that we can catch if a promise fails
    try {
      const _result = await promise();
      console.log(_result);
      
      // Break out of loop if promise returns results that fails predicate
      if (!predicate(_result))
        break;
      else
        result = _result;
        
    } catch(e) {
      // Break out of loop if promise fails
      break;
    }
  }
  console.log(`Final result: ${result}`);
  return result;
}

// Our predicate is simply if the promise returns a random number > 0.5
awaitSequentialPromises([testPromise, testPromise, testPromise], v => v > 0.5);

答案 2 :(得分:-1)

这是应该执行您想要的功能。您可能需要修改它,以不同方式处理失败的谓词。

正如其他人指出的那样,您不能“取消”诺言,因为它通常代表已经开始的事情。

/**
 * Combines an array of promises with a predicate. Works like Promise.all, but checks each result from the given
 * Promise against the supplied predicate, and if the result value from any Promise fails the predicate, the
 * overall promise is immediatly resolved with those values which passed the predicate.
 */
function allWithPredicate(
    promises /*: Array<Promise<T1>, Promise<T2>, ...> */,
    predicate /*: (mostRecentResult, allResults, promiseIndex)*/
) /*: Promise<[T1, T2, ...]> */ {

    // Handle empty input arrays
    if (promises.length === 0)
        return Promise.resolve([]);

    // Create a manual result promise, as none of the built-in promise manipulation functions will do the trick.
    return new Promise(
        (resolve, reject) => {
            // This array will collect the values from the promises. It needs to be the same size as the array of
            // promises.
            const results = new Array(promises.length);

            // We track the number of resolved promises so we know when we're done. We can't use the length of the
            // array above because the last promise might finish first, which would cause the array to be equal in
            // length to the promises array.
            let resolvedCount = 0;

            // Now we go through each promise and set things up
            promises.forEach(
                (promise, index) => {
                    promise.then(
                        result => {
                            // A promise has finished successfully! Hooray. We increment the counter.
                            resolvedCount++;

                            // Now we check if the newly returned value from the promise is acceptable, using the
                            // supplied predicate.
                            if (predicate(result, results, index)) {
                                // If if it, we keep this result.
                                results[index] = result;

                                // If the resolved counter is equal to the original number of promises in the array,
                                // we are done and can return. Note that we do NOT check against the length of
                                // the promises array, as it may have been mutated since we were originally called.
                                if (resolvedCount === results.length) {
                                    resolve(results);
                                }
                            } else {
                                // If not, we short-circuit by resolving the overall promise immediately.
                                // Note that as written, we do NOT include the unacceptable value in the result.
                                resolve(results);
                            }
                        },
                        error => {
                            reject(error);
                        }
                    )
                }
            )
        }
    );
}

以下是jsbin演示的链接:https://jsbin.com/jasiguredu/edit?js,console

答案 3 :(得分:-2)

Promise.all仅接受一个论点

var promises = [promise1, promise2, promise3];
Promise.all(promises)
  .then(p => {
      if(p === false) {
          throw new Error(false);
      } else {
          console.log(p);
      }
  })
  .catch(error => { 
      console.log(error);
  });
}

如果Promise.all中的某个承诺以catch语句结尾,然后完成,那么您可以根据需要在该承诺之外设置一个计数器。