Wait for Multiple Promises to Finish

Wait until all promises complete even if some rejected

Benjamin's answer offers a great abstraction for solving this issue, but I was hoping for a less abstracted solution. The explicit way to to resolve this issue is to simply call .catch on the internal promises, and return the error from their callback.

let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });

Promise.all([c, d])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));

Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));

Taking this one step further, you could write a generic catch handler that looks like this:

const catchHandler = error => ({ payload: error, resolved: false });

then you can do

> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]

The problem with this is that the caught values will have a different interface than the non-caught values, so to clean this up you might do something like:

const successHandler = result => ({ payload: result, resolved: true });

So now you can do this:

> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]

Then to keep it DRY, you get to Benjamin's answer:

const reflect = promise => promise
.then(successHandler)
.catch(catchHander)

where it now looks like

> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]

The benefits of the second solution are that its abstracted and DRY. The downside is you have more code, and you have to remember to reflect all your promises to make things consistent.

I would characterize my solution as explicit and KISS, but indeed less robust. The interface doesn't guarantee that you know exactly whether the promise succeeded or failed.

For example you might have this:

const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));

This won't get caught by a.catch, so

> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]

There's no way to tell which one was fatal and which was wasn't. If that's important then you're going to want to enforce and interface that tracks whether it was successful or not (which reflect does).

If you just want to handle errors gracefully, then you can just treat errors as undefined values:

> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]

In my case, I don't need to know the error or how it failed--I just care whether I have the value or not. I'll let the function that generates the promise worry about logging the specific error.

const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});

That way, the rest of the application can ignore its error if it wants, and treat it as an undefined value if it wants.

I want my high level functions to fail safely and not worry about the details on why its dependencies failed, and I also prefer KISS to DRY when I have to make that tradeoff--which is ultimately why I opted to not use reflect.

Wait for multiple promises to finish

You can use

removeAll() {
Promise.all([
this.storage.remove(key1),
this.storage.remove(key2),
this.storage.remove(key3),
]).then(value => doSomething());

See also https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

How to wait for all Promises to be finished

First, you have to fix both functionOne() and functionTwo() so that they resolve() the promise they create when their timer fires. Without that, they just create a promise that never resolves which isn't very useful and does not notify the caller when they are done.

Then, to run them in parallel, use Promise.all() which will let you call both functions and then it will track both returned promises together and the promise returned from the call to Promise.all() will resolve when both functions have completed or reject if either one of the functions rejected their promise.

If your example here, none of your promises rejects, but if you want to know when all promises have finished even if some reject, then you would use Promise.allSettled() instead of Promise.all(). The main difference is that Promise.all() will short-circuit and reject it's promise as soon as any promise you pass it rejects, whereas Promise.allSettled() will wait until all promises are done, regardless of resolve/reject. Though you aren't using it here, the resolved value from Promise.allSettled() is different also so that you can tell which promises rejected and which resolved.

Here's a runnable (in the snippet) example that uses Promise.all().
You can swap in Promise.allSettled() if that's the behavior you'd rather see:

    const logger = console;

const functionOne = function () {
logger.info("Starting functionOne");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
resolve();
}, 20000);
});
};

const functionTwo = function () {
logger.info("Starting functionTwo");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionTwo after 10 sec.");
resolve();
}, 10000);
});
};
const runningFunctions = function () {
logger.info('Start jobs');
return Promise.all([functionOne(), functionTwo()]);
}

runningFunctions().then(() => {
logger.info(`All done after 20 sec.`);
}).catch(err => {
console.log(err);
});

How do I wait for multiple promises to resolve but also include a set minumum delay?

Forcing either one or both axios.get() functions to complete below some time limit (other than failing on a timeout), isn't doable, since you don't control the transport.

One thing you can do is force one or more functions to complete at or after some time threshold, with something like this...

function padToTime(promise, interval) {
// delay returns a promise that resolves after an interval
const delay = interval => new Promise(resolve => setTimeout(resolve, interval));
// caller can provide a singular or an array of promises, avoiding the extra .all
let promises = Array.isArray(promise) ? promise : [promise];
return Promise.all([...promises, delay(interval)])
.then(results => results.slice(0, -1));
}

EDIT good idea from @VLAZ to append an extra promise that forces minimal time (then slice its result off later).

The caller can say:

async function getXml(){
// op function, no calls to "delay"
}

async function getPeople(){
// op function, no calls to "delay"
}

// run these both together, have them take *no fewer than* 10s
padToTime([getXml(),getPeople()], 10000).then(results => {
// we'll get here in no fewer than 10sec with the promise all results
})

How do I await multiple promises in-parallel without 'fail-fast' behavior?

ES2020 contains Promise.allSettled, which will do what you want.

Promise.allSettled([    Promise.resolve('a'),    Promise.reject('b')]).then(console.log)

How to wait for a loop with promises to complete to execute a method?

setTimeout is asynchronous, but is not thenable like a promise. If your direct question is to execute your code when all the timeouts end, then it should be done as follows:

const array = ['a', 'b', 'c'];
let numberOfTimeoutsToBeExecuted = array.length;
// keep track of the number of timeouts that have finished execution
let numberOfTimeoutsExecuted = 0;

console.log('start');
array.forEach((i) => {
setTimeout(() => {
console.log(i),
numberOfTimeoutsExecuted += 1;
// execute your code when the last timeout has finished execution
if (numberOfTimeoutsToBeExecuted === numberOfTimeoutsExecuted) {
console.log('end');
}
}, 3000);
});

However, if you want to wait for multiple promises to finish execution, the following is the best way to do this:

const array = ['a', 'b', 'c'];
const promises = [];

console.log('start');
array.forEach((i) => {
// add all promises that will be executed into array
promises.push(new Promise((resolve, reject) => {
setTimeout(()=> {
console.log(i);
resolve();
}, 3000);
}));
});

// execute all promises here
Promise.all(promises).then(() => console.log('end'));

How to wait for 2 of Promises in Promise.all to finish and then do another task

In a comment you seem to have said you specifically know that in advance which two are more urgent than the rest (and that in this example, it's Task1 and Task4).

Then just use Promise.all twice:

const allResults = Promise.all([
Promise.all([Task1(), Task4()])
.then(([result1, result4]) => {
// Those two are done, do what you like with `result1` and `result4`...
return [result1, result4];
}),
Task2(),
Task3(),
Task5()
])
.then(([[result1, result4], result2, result3, result5]) => {
// All five are done now, let's put them in order
return [result1, result2, result3, result4, result5];
})
.then(/*...*/)
.catch(/*...*/);

In there, I've preserved the overall 1, 2, 3, 4, 5 order in the outer chain by remapping the order in the overall then handler.


Originally, I assumed you wanted to wait until any two have finished, rather than a specific two. There's no built-in for that, but it's easy enough to write:

function enough(promises, min) {
if (typeof min !== "number") {
return Promise.all(promises);
}
let counter = 0;
const results = [];
return new Promise((resolve, reject) => {
let index = 0;
for (const promise of promises) {
let position = index++;
promise.then(
result => {
results[position] = result;
if (++counter >= min) {
resolve(results);
}
},
reject
);
}
});
}

Live Example:

function enough(promises, min) {  if (typeof min !== "number") {    return Promise.all(promises);  }  let counter = 0;  const results = [];  return new Promise((resolve, reject) => {    let index = 0;    for (const promise of promises) {      let position = index++;      promise.then(        result => {          results[position] = result;          if (++counter >= min) {            resolve(results);          }        },        reject      );    }  });}
const delay = (ms, ...args) => new Promise(resolve => setTimeout(resolve, ms, ...args));const rnd = () => Math.random() * 1000;
enough( [ delay(rnd(), "a"), delay(rnd(), "b"), delay(rnd(), "c"), delay(rnd(), "d"), delay(rnd(), "e") ], 2).then(results => { console.log(results);}).catch(error => { console.error(error);});

How to return many Promises and wait for them all before doing other stuff

You can use Promise.all (spec, MDN) for that: It accepts a bunch of individual promises and gives you back a single promise that is resolved when all of the ones you gave it are resolved, or rejected when any of them is rejected.

So if you make doSomeAsyncStuff return a promise, then:

    const promises = [];
// ^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− use `const` or `let`, not `var`

for (let i = 0; i < 5; i++) {
// ^^^−−−−−−−−−−−−−−−−−−−−−−−− added missing declaration
promises.push(doSomeAsyncStuff());
}

Promise.all(promises)
.then(() => {
for (let i = 0; i < 5; i++) {
// ^^^−−−−−−−−−−−−−−−− added missing declaration
doSomeStuffOnlyWhenTheAsyncStuffIsFinish();
}
})
.catch((e) => {
// handle errors here
});

MDN has an article on promises here. I also cover promsies in detail in Chapter 8 of my book JavaScript: The New Toys, links in my profile if you're interested.

Here's an example:

 function doSomethingAsync(value) {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Resolving " + value);
resolve(value);
}, Math.floor(Math.random() * 1000));
});
}

function test() {
const promises = [];

for (let i = 0; i < 5; ++i) {
promises.push(doSomethingAsync(i));
}

Promise.all(promises)
.then((results) => {
console.log("All done", results);
})
.catch((e) => {
// Handle errors here
});
}

test();


Related Topics



Leave a reply



Submit