Skip to the content.

Async and arrays

Sequential for

Use for for sequential async function execution

const sequentialAsync = async () => {
  const data = [5, 7, 3, 10, 40];

  const apiCall = (ms: number): Promise<number> =>
    new Promise((res) => {
      console.log(`-> ${ms}`);
      setTimeout(() => {
        console.log(`<- ${ms}`);
        res(ms * 100);
      }, ms);
    });

  const asyncRes = [];
  for (const i of data) {
    asyncRes.push(await apiCall(i));
  }
  console.log(asyncRes);
};
sequentialAsync();

open code in online editor

Expected output:

-> 5
<- 5
-> 7
<- 7
-> 3
<- 3
-> 10
<- 10
-> 40
<- 40
[ 500, 700, 300, 1000, 4000 ]

Parallel async map

Using .map for parallel async functions execution

Remember: First rejection will throw an error in function without waiting for others to finish.

This behavior usually is what is expected: Fails as soon as any request fails.

const parallelAsyncMap = async () => {
  const data = [5, 7, 3, 10, 40];

  const apiCall = (ms: number): Promise<number> =>
    new Promise((res) => {
      console.log(`-> ${ms}`);
      setTimeout(() => {
        console.log(`<- ${ms}`);
        res(ms * 100);
      }, ms);
    });

  const asyncRes = await Promise.all(
    data.map(async (i) => {
      return await apiCall(i);
    })
  );
  console.log(asyncRes);
};
parallelAsyncMap();

open code in online editor

Pay attention that <- are happening in parallel and returning in order (low to high). But overall results array is built from corresponding results (not ordered)

-> 5
-> 7
-> 3
-> 10
-> 40
<- 3
<- 5
<- 7
<- 10
<- 40
[ 500, 700, 300, 1000, 4000 ]

Async reduce

Using async function in reduce.

const sequentialData = [5, 10, 15, 95, 150];

async function sequentialAsyncReduce() {
  const apiCall = (ms: number): Promise<number> =>
    new Promise((res) => {
      console.log(`-> ${ms}`);
      setTimeout(() => {
        console.log(`<- ${ms}`);
        res(ms * 100);
      }, ms);
    });

  const results = await sequentialData.reduce(
    async (acc: Promise<number[]>, val: number) => {
      const results = await acc;
      await apiCall(val);
      return [...results, val];
    },
    Promise.resolve([])
  );
  console.log(`Results ${results}`);
}
sequentialAsyncReduce();

open code in online editor

Expected output:

-> 5
<- 5
-> 10
<- 10
-> 15
<- 15
-> 95
<- 95
-> 150
<- 150
Results 5,10,15,95,150

Use .map and .reduce to apply async function over array elements in groups.

const chunk = (input: number[], count = 10): number[][] =>
  input.length > 0
    ? [input.slice(0, count), ...chunk(input.slice(count), count)]
    : [];

async function parallelAsyncReduce() {
  const data = [5, 10, 15, 20, 25, 40, 50, 100, 120, 150, 100, 90, 95, 150];

  const apiCall = (ms: number): Promise<number> =>
    new Promise((res) => {
      console.log(`-> ${ms}`);
      setTimeout(() => {
        console.log(`<- ${ms}`);
        res(ms * 100);
      }, ms);
    });

  const mapChunks = (
    localData: number[],
    cb: (i: number) => Promise<number>,
    size = 5
  ): Promise<number[]> => {
    const batches = chunk(localData, size);
    return batches.reduce(async (acc: Promise<number[]>, batch: number[]) => {
      const arr = await acc;
      const thisBatch = await Promise.all(batch.map(cb));
      return [...arr, ...thisBatch];
    }, Promise.resolve([]));
  };

  const result = await mapChunks(data, async (value: number) => {
    return await apiCall(value);
  });
  console.log({ result });
}
parallelAsyncReduce();

open code in online editor

Expected output:

-> 5
-> 10
-> 15
-> 20
-> 25
<- 5
<- 10
<- 15
<- 20
<- 25

-> 40
-> 50
-> 100
-> 120
-> 150
<- 40
<- 50
<- 100
<- 120
<- 150

-> 100
-> 90
-> 95
-> 150
<- 90
<- 95
<- 100
<- 150
{
result: [
500,  1000,  1500,
2000,  2500,  4000,
5000, 10000, 12000,
15000, 10000,  9000,
9500, 15000
]
}