I'm using async
/await
to fire several api
calls in parallel:
async function foo(arr) {
const results = await Promise.all(arr.map(v => {
return doAsyncThing(v)
}))
return results
}
I know that, unlike loops
, Promise.all
executes in-parallel (that is, the waiting-for-results portion is in parallel).
But I also know that:
Promise.all is rejected if one of the elements is rejected and Promise.all fails fast: If you have four promises which resolve after a timeout, and one rejects immediately, then Promise.all rejects immediately.
As I read this, if I Promise.all
with 5 promises, and the first one to finish returns a reject()
, then the other 4 are effectively cancelled and their promised resolve()
values are lost.
Is there a third way? Where execution is effectively in-parallel, but a single failure doesn't spoil the whole bunch?
While the technique in the accepted answer can solve your issue, it's an anti-pattern. Resolving a promise with an error isn't good practice and there is a cleaner way of doing this.
What you want to do, in pseudo-code, is:
fn task() {
result-1 = doAsync();
result-n = doAsync();
// handle results together
return handleResults(result-1, ..., result-n)
}
This can be achieved simply with async
/await
without the need to use Promise.all
. A working example:
console.clear();
function wait(ms, data) {
return new Promise( resolve => setTimeout(resolve.bind(this, data), ms) );
}
/**
* These will be run in series, because we call
* a function and immediately wait for each result,
* so this will finish in 1s.
*/
async function series() {
return {
result1: await wait(500, 'seriesTask1'),
result2: await wait(500, 'seriesTask2'),
}
}
/**
* While here we call the functions first,
* then wait for the result later, so
* this will finish in 500ms.
*/
async function parallel() {
const task1 = wait(500, 'parallelTask1');
const task2 = wait(500, 'parallelTask2');
return {
result1: await task1,
result2: await task2,
}
}
async function taskRunner(fn, label) {
const startTime = performance.now();
console.log(`Task ${label} starting...`);
let result = await fn();
console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result);
}
void taskRunner(series, 'series');
void taskRunner(parallel, 'parallel');
Note: You will need a browser which has async
/await
enabled to run this snippet.
This way you can use simply try
/ catch
to handle your errors, and return partial results inside parallel
function.