Skip to content

Instantly share code, notes, and snippets.

@Phoenix35
Last active January 14, 2023 16:57
Show Gist options
  • Save Phoenix35/f8f5cb91c8c517df337bc0082661e6db to your computer and use it in GitHub Desktop.
Save Phoenix35/f8f5cb91c8c517df337bc0082661e6db to your computer and use it in GitHub Desktop.
Waits until a specified number of promises is resolved/fulfilled
export async function waitForNFulfilled (n, ...promises) {
const { length } = promises;
if (n >= length)
return Promise.all(promises);
const leftPromises = [];
const tallyPromises = [];
// Promises can resolve on the same tick, so we need to mark the fulfilled ones in parallel
async function tallySettled (promise) {
try {
await promise;
// Dynamic, so passing the index to the tally function would not work
tallyPromises[leftPromises.indexOf(promise)] = true;
} catch {}
}
for (let i = 0; i < length; ++i) {
leftPromises.push(tallySettled(promises[i]));
tallyPromises.push(false);
}
const promisesFulfilled = [];
for (;;) {
// eslint-disable-next-line no-await-in-loop
await Promise.race(leftPromises);
for (let i = leftPromises.length - 1; i >= 0; --i) {
if (tallyPromises[i]) {
const [ fulfilledPromise ] = leftPromises.splice(i, 1);
tallyPromises.splice(i, 1);
if ((promisesFulfilled.push(fulfilledPromise)) === n)
return Promise.all(promisesFulfilled);
// TODO: throw an aggregate error when the count cannot be achieved anymore
// number of rejections + n > number of promises
}
}
}
}
export async function waitForNSettled (n, ...promises) {
const { length } = promises;
if (n >= length)
return Promise.allSettled(promises);
const leftPromises = [];
const tallyPromises = [];
// Promises can resolve on the same tick, so we need to mark the resolved ones in parallel
async function tallySettled (promise) {
try {
await promise;
} catch {}
// Dynamic, so passing the index to the tally function would not work
tallyPromises[leftPromises.indexOf(promise)] = true;
}
for (let i = 0; i < length; ++i) {
leftPromises.push(tallySettled(promises[i]));
tallyPromises.push(false);
}
const promisesSettled = [];
for (;;) {
// eslint-disable-next-line no-await-in-loop
await Promise.race(leftPromises);
for (let i = leftPromises.length - 1; i >= 0; --i) {
if (tallyPromises[i]) {
const [ resolvedPromise ] = leftPromises.splice(i, 1);
tallyPromises.splice(i, 1);
if ((promisesSettled.push(resolvedPromise)) === n)
return Promise.allSettled(promisesSettled);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment