How do I use promises with Reduce and how do I choose between serial and parallel processing

How to Use Async Functions with array. reduce in Javascript – Tamas Sallai

In the first article, we showed how async/await helps execute asynchronous commands, but not when processing collections asynchronously. In this article, we will examine the Reduce function, which is the most feature-rich set function because it emulates all the other functions.

1. Array.reduce

Reduce iteratively constructs a value and returns it, which is not necessarily a set. This is where the name comes from, because it reduces the number of values collected.

The iteration function retrieves the previous result (called by Memo in the example below) as well as the current value E.

The following function sums the elements, starting at 0 (the second reduce argument) :

const arr = [1.2.3];



const syncRes = arr.reduce((memo, e) = > {

 return memo + e;

}, 0);



console.log(syncRes);

/ / 6

Copy the code
memo e The results of
0 (initial) 1 1
1 2 3
3 3 Final result 6
The reduce function

2. The asynchronousreduce

The asynchronous version is almost identical, but each iteration returns a Promise, so the memo will be a Promise of the previous result. The iterating function needs await it to evaluate the next result:

// utility function for sleeping

const sleep = (n) = > new Promise((res) = > setTimeout(res, n));



const arr = [1.2.3];



const asyncRes = await arr.reduce(async (memo, e) => {

 await sleep(10);

 return (await memo) + e;

}, 0);



console.log(asyncRes);

/ / 6

Copy the code
memo e The results of
0 (initial) 1 Promise (1)
Promise (1) 2 Promise (3)
Promise (3) 3 (6)
Async reduce function

The structure async (memo, e) => await memo, reduce can handle any asynchronous function and can await editing it.

3. The timing

There is an interesting property when concurrency occurs in Reduce. In the synchronous version, elements are processed one-to-one, which is not surprising because they depend on previous results. However, when asynchronous Reduce runs, all iteration functions will start running in parallel, and await the memo only when needed.

3.1 await memo last

In the example above, all the sleeps are executed in parallel because the await Memo causes the function to wait for the last function to complete.

const arr = [1.2.3];



const startTime = new Date().getTime();



const asyncRes = await arr.reduce(async (memo, e) => {

 await sleep(10);

 return (await memo) + e;

}, 0);



console.log(`Took The ${new Date().getTime() - startTime} ms`);

// Took 11-13 ms

Copy the code
Async reduce with “await memo” last

3.2 await memo first

But when await Memo comes first, these functions run in order:

const arr = [1.2.3];



const startTime = new Date().getTime();



const asyncRes = await arr.reduce(async (memo, e) => {

 await memo;

 await sleep(10);

 return (await memo) + e;

}, 0);



console.log(`Took The ${new Date().getTime() - startTime} ms`);

// Took 36-38 ms

Copy the code
Async reduce with “await memo” first

This behavior is usually not a problem, meaning that everything that doesn’t depend on the previous result will be computed immediately, with only the dependent part waiting for the previous value.

3.3 When parallelism is important

But in some cases, doing something ahead of time may not be feasible.

For example, I have a piece of code that prints different PDFS and splices them together into a file using the PDF-lib library.

Implementing printPDF parallel running resource-intensive features:

const result = await printingPages.reduce(async (memo, page) => {

 const pdf = await PDFDocument.load(await printPDF(page));



 const pdfDoc = await memo;



 (await pdfDoc.copyPages(pdf, pdf.getPageIndices()))

  .forEach((page) = > pdfDoc.addPage(page));



 return pdfDoc;



}, PDFDocument.create());

Copy the code

I’ve noticed that when I have a lot of pages to print, it takes up too much memory and slows down the whole process.

A simple change to make the printPDF call wait for the previous call to complete:

const result = await printingPages.reduce(async (memo, page) => {

 const pdfDoc = await memo;



 const pdf = await PDFDocument.load(await printPDF(page));



 (await pdfDoc.copyPages(pdf, pdf.getPageIndices()))

  .forEach((page) = > pdfDoc.addPage(page));



 return pdfDoc;



}, PDFDocument.create());

Copy the code

4. Conclusion

Reduce functions can easily be converted to asynchronous functions, but figuring out parallelism can be tricky. Fortunately, it rarely breaks anything, but in some resource-intensive or rate-constrained operations, knowing how to call a function is critical.

Recommended reading

  • How to use Async correctly in array.foreach

  • How to use Async correctly in array.filter

The shadow of Perso (^_-)