I need to make an unknown number of async calls in parallel via Promise.all. Something similar to this:

let externalCalls = [call1()];
if (someCondition1) {
  externalCalls.push(call2());
}
if (someCondition2) {
  externalCalls.push(call3());
}

Then externalCalls will be passed to Promise.all so they can be run in parallel.

Ideally, I'd like to use destructuring of the results so that I can refer to results by name, i.e.

const [call1, call2, call3] = await Promise.all(externalCalls);

I know call1 will always be there, but I don't know if call2 or call3 will be there. So I'd like to define the const result of calling await Promise.all dynamically to have the correct properties, Is this possible? Or am I stuck having a generic results array of unknown length, and then having to inspect each item in the results to see which call produced it?

3 Answers

1
ııı On Best Solutions

On the one hand, you already know which calls were .push()ed to externalCalls, based on someCondition1, etc.

But perhaps it's better to build externalCalls in a different way so it always has the same length:

const conditions = [true, someCondition1, etc]
const calls = [call1, call2, etc]
const externalCalls = conditions.map(
  (c, i) => c ? calls[i]() : Promise.resolve(null))
const [result1, result2, etc] = await Promise.all(externalCalls)
1
Mohammad altenji On

there is no direct way to know the source of the response of promise.all.

but you can add more information to the response of call1, call2, call3

so your modified code would look like that:

let call1 = fetch(url).then(data1 => ({...data1, source: 'call1'}));
let call2 = fetch(url).then(data2 => ({...data2, source: 'call2'}));
let call3 = fetch(url).then(data3 => ({...data3, source: 'call3'}));
let externalCalls = [call1, call2, call3, etc..];

and in your promise.all response, you can check the source of each returned response like that"

let returnedData = {};
Promise.all(externalCalls).then(allData => {
    for(let data of allData){
        returnedData[data.source] = data;
        console.log(returnedData);
    }
});

and when you console returnedData will get something like that:

 returnedData = { 
    'call1': { call1 related data},
    'call2': { call2 related data},
    'call3': { call3 related data},
}
1
George On

If someCondition1 is false, you don't push anything, then if someCondition2 is true, you push call3(), so you should expect call3 to be in the second item of the returned array. Therefore, instead you can just return undefined for calls which have no value, keeping the calls in the array having synchronised indexes.

let someCondition1 = false;
let someCondition2 = true;
let call1 = () => Promise.resolve("hello");
let call2 = () => Promise.resolve("world");
let call3 = () => Promise.resolve(":)");
let externalCalls = [
  call1(),
  someCondition1 ? call2() : undefined,
  someCondition2 ? call3() : undefined
];
async function resolveCalls(calls){
  const [call1, call2, call3] = await Promise.all(calls);
  console.log("call1:", call1);
  console.log("call2:", call2);
  console.log("call3:", call3);
}
resolveCalls(externalCalls);