JavaScript Promise.all с предикатом - PullRequest
0 голосов
/ 11 января 2019

Сначала дан массив со списком обещаний:

var promises = [promise1, promise2, promise3];

Я бы хотел выполнить все эти обещания с помощью предиката. Если первый предикат возвращает false, немедленно возвращает результат, давая результат того, что было обработано до этого момента, и отменяет все оставшиеся обещания. Например что-то вроде:

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

Результаты должны иметь то, что было обработано до того момента, когда первый предикат потерпел неудачу.

Обещания должны обрабатываться последовательно, а не параллельно.

Ответы [ 4 ]

0 голосов
/ 11 января 2019

Мое решение очень похоже на одно обещание Йоны, но в том смысле, что вы возвращаете только последнее возвращенное значение обещания, которое удовлетворяет предикату.

В приведенном ниже коде я просто создал тестовое обещание, которое возвращает случайное число через 500 мс. Затем случайное число передается в предикат, который возвращает значение true, превышающее 0,5.

Улучшение заключается в том, что мы пытаемся поймать событие, когда обещание может быть не выполнено. Если обещание не выполняется, цепочка немедленно прерывается, возвращая последний результат, аналогично тому, как обещание возвращает результат, который не соответствует предикату:

// 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);
0 голосов
/ 11 января 2019

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, то оно заканчивается, если вам нужно, вы можете иметь счетчик вне обещания.

0 голосов
/ 11 января 2019

Вот функция, которая должна делать то, что вы хотите. Возможно, вы захотите изменить его так, чтобы он по-разному обрабатывал ошибочные предикаты.

Как уже отмечали другие, вы не можете "отменить" обещание, поскольку оно представляет собой нечто, что уже началось, вообще говоря.

/**
 * 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

0 голосов
/ 11 января 2019

Вы не можете отменить обещание. Однако вы можете просто начать следующее действие, когда предыдущее будет сделано:

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