見出し画像

Array.prototype.reduce can be used to keep the order of async processes

In an extreme edge case, I sometimes want to keep the order of async processes.
For example, you have to render an image from some layers like Photoshop.

const layers = [
  { imageURL: 'http://example.com/dog.png', title: 'dog' },
  { imageURL: 'http://example.com/ball.png', , title: 'ball' },
  { imageURL: 'http://example.com/park.png', title: 'park' },
];

In this case, the park should be placed most under layer and the ball and the dog should be above it. So, probably you would reverse the array and render to a combined image.

const reversed = layer.reverse();
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');

const promises = reversed.map(async (layer) => {
  const res = await get(layer.imageURL);
  context.drawImage(res.image);
});
await Promise.all(promises);

However, This canvas might be different each time since we can't control which HTTP response would finish first. Promise.all doesn't guarantee the order of its promises' array.

Therefore, we have to use another approach. But how? If you were very familiar with Array.prototype.reduce function, you would already notice the solution.
The approach I found is that wait for the previous promise at first of each iteration.

await reversed.reduce(async (prev, layer) => {
  await prev;
  const res = await get(layer.imageURL);
  context.drawImage(res.image);
}, Promise.resolve());

For the first iteration, we have to give an empty promise object, which has been already resolved, since there's no HTTP request yet. Don't forget to put "await" keyword before the part of the calling the reduce function.