node.jstypescriptasync-awaitaxiospromise

Using promise.all with .then


I want to use await Promise.all with axios so that the requests can be sent concurrently. I have a map with the key as an id, and the value as an url. I want to retain the output also in a similar map, the key would still be the id but the value would be the response (arraybuffer).

Following is what I got suggested by a LLM:

async function fetchImages(
  idUrlMap: Map<string, string>
): Promise<Map<string, ArrayBuffer>> {
  // Convert the map into an array of promises
  const promises = Array.from(idUrlMap.entries()).map(([id, url]) => {
    return axios
      .get(url, { responseType: 'arraybuffer' })
      .then(response => ({ id, data: response.data }));
  });

  // Resolve all promises concurrently
  const results = await Promise.all(promises);

  // Convert the results back into a map
  const resultMap = new Map<string, ArrayBuffer>();
  results.forEach(({ id, data }) => {
    resultMap.set(id, data);
  });

  return resultMap;
}

What I'm wondering is -- we are doing axios.get().then() for each array element, which will resolve the promise as soon as it's ready. So the promises won't actually get aggregated and get resolved at Promise.all. Hence I won't achieve concurrency.

Is my understanding correct? Why/why not?


Solution

  • I would suggest you run the following code to see the behavior yourself:

    async function fetchImages(idUrlMap) {
        // Convert the map into an array of promises
        const promises = Array.from(idUrlMap.entries()).map(([id, url]) => {
          return new Promise((resolve) => {
            setTimeout(() => {
                console.log(id, 'Triggered timeout');
                resolve();
            }, 3000-id*1000)
          }).then(() => {
            console.log('In then for', id);
            return { id }
          })
        });
      
        // Resolve all promises concurrently
        const results = await Promise.all(promises);
        console.log('Results finished', results);
    }
    
    fetchImages(Object.entries({'1': 'test1', '2': 'test2', '3': 'test3'}));
    

    This is a simplified version of yours which uses setTimeout instead of axios, but the same principle applies. You'll notice that results ends up being an array of the value returned in your then callback. In other words, it all runs as you would expect, no overthinking required.

    Expected output:

    2 Triggered timeout
    In then for 2
    1 Triggered timeout
    In then for 1
    0 Triggered timeout
    In then for 0
    Results finished [ { id: 0 }, { id: 1 }, { id: 2 } ]