Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 Promise wait for K out N promises to resolve

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
});
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});

Promise.all([p1,p2,p3]).then(values => { 
  console.log(values);
}, reason => {
  console.log(reason)
});

How can i wait for 2 promises to get completed ? Promise.race() wait for one promise to get completed.

Edit

I have n number of promises, what i want to achieve is wait for first k number of promises to get resolved and than trigger some event. assume k < n

Edit - 2

I am sure that k number of promise will be successfully resolved out of n numbers of promise given

like image 206
Santanu Sahoo Avatar asked Jan 06 '18 16:01

Santanu Sahoo


2 Answers

(Note: Bluebird has a built-in helper function that serves precisely this purpose and has more or less the same behavior as my waitForN method below, so that option is always available)

I don't believe ES6 promises have an elegant built-in way to do this, but you could define the following relatively short now somewhat long helper function to take care of this.

Edit: Added an additional helper function that includes the indices of the successful promises in the result.

Edit: Updated to reject if the number of rejections reaches the point where successful resolution would be impossible.

Edit: Updated to more gracefully work with promises if it is iterable and check edge cases where the result should immediately resolve (e.g. if n === 0)

function waitForN(n, promises) {
    let resolved = [];
    let failCount = 0;
    let pCount = 0;

    return new Promise((resolve, reject) => {
        const checkSuccess = () => {
            if (resolved.length >= n) { resolve(resolved); }
        };
        const checkFailure = () => {
            if (failCount + n > pCount) {
                reject(new Error(`Impossible to resolve successfully. n = ${n}, promise count = ${pCount}, failure count = ${failCount}`));
            }
        };

        for (let p of promises) {
            pCount += 1;

            Promise.resolve(p).then(r => {
                if (resolved.length < n) { resolved.push(r); }
                checkSuccess();
            }, () => {
                failCount += 1;
                checkFailure();
            });
        }

        checkFailure();
        checkSuccess();
    });
}

const waitForNWithIndices = (n, promises) =>
    waitForN(n, promises.map((p, index) => 
        Promise.resolve(p).then(result => ({ index, result }))
    ));

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
});
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1500, 'four');
});

waitForN(2, [p1,p2,p3,p4]).then(values => { 
  console.log(values);
}, reason => {
  console.log(reason)
});

waitForNWithIndices(2, [p1,p2,p3,p4]).then(values => { 
  console.log(values);
}, reason => {
  console.log(reason)
});
like image 52
JLRishe Avatar answered Nov 04 '22 13:11

JLRishe


You could use a higher order function to bake in the n then run all promises waiting for n to finish

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
});
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});

const raceN = n => (...promises) => {
  const resolved = []
  return new Promise((res, rej) =>
    promises.forEach(promise => 
      promise.then(x => {
        resolved.push(x)
        if (resolved.length === n)
          res(resolved)
      })
      .catch(rej)
    )
  )
}

const race2 = raceN(2)
race2(p1, p2, p3)
  .then(results => console.log(results))
  
race2(Promise.resolve('resolved'), Promise.reject('rejected'))
  .then(results => console.log(results))
  .catch(reason => console.log(reason))
like image 35
synthet1c Avatar answered Nov 04 '22 12:11

synthet1c