MSD MSD - 1 month ago 6
Javascript Question

How to take the first N Promises that pass a filter?

If I have an array of values, and I use those values to perform a check that performs a promise, how do I take the first N Promises that pass a check?

For the following example, I just want the first three odd numbers, as determined by the function,

isOdd
, that returns a promise.



const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const len = collection.length;
const results = [];
let index = -1;

function isOdd(value) {
if (value % 2 !== 0) {
return Promise.resolve(1);
} else {
return Promise.resolve(0);
}
}

while (++index < len) {
const currentValue = collection[index];

const result = isOdd(currentValue);

// append the result to an array of results IF Promise.resolve's value === 1

if (results.length >== 3) {
break;
}
}

Promise.all(results).then(doMoreStuff);

Answer

I'm restating the problem a bit to assume that the "filter" you mention is actually a promise being fulfilled vs. rejected, not fulfilling with 0 vs. 1. In other words:

function isOdd(value) {
  if (value % 2 !== 0) {
    return Promise.resolve();
  } else {
    return Promise.reject();
  }
}

You just need to write the logic.

function takeFirstN(promises, n) {
  const results = [];

  if (!promises.length) return Promise.resolve([]);

  return new Promise(resolve => {
    for (promise of promises) {
      promise.then(result => {
        results.push(result);
        if (!(--n)) resolve(results);
      });
    }
  });
}

In other words, kick off all the promises. As each one resolves, put its result on a list of results, and check to see if n promises have resolved, in which case resolve the overall promise.

Then:

const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
takefirstN(collection.map(isOdd), 3)
  .then(results => console.log(results));

This does not deal with the case where less than n promises fulfill, or handle edge cases such as n being greater than the number of promises. That is left as an exercise.

Original problem

If you want to stick with your definition of promises which fulfill with a value of either 0 or 1, then arrange to pass a predicate (called filter here) to a takeFirstNWhich function as follows:

function takeFirstNWhich(promises, filter, n) {
  const results = [];

  if (!promises.length) return Promise.resolve([]);

  return new Promise(resolve => {
    for (promise of promises) {
      promise.then(result => {
        if (filter(result)) {
          results.push(result);
          if (!(--n)) resolve(results);
        }
      });
    }
  });
}

Now you can write:

takefirstNWhich(collection.map(isOdd), odd => odd, 3)
  .then(results => console.log(results));
Comments