How to run asynchronous loops in order or in parallel in JavaScript?
Before doing asynchronous magic, I want to remind you of the classic synchronous loops.
A long time ago I wrote cycles in this way (maybe you too):
for (var i=0; i < array.length; i++) { var item = array[i]; // - item }
This cycle is good and fast. But he has a lot of problems with readability and support. After a while, I got used to his better version:
array.forEach((item) => { // - item });
JavaScript language is developing very quickly. There are new features and syntax. One of my favorite improvements is async / await .
Now I use this syntax quite often. And sometimes there are situations when I need to do something with the elements of the array asynchronously.
How to use await
in a loop body? Let's just try to write an asynchronous function and wait for the task of processing each element:
async function processArray(array) { array.forEach(item => { // // ! await func(item); }) }
This code will generate an error. Why? Because we cannot use await
inside a synchronous function. As you can see, processArray
is an asynchronous function. But the anonymous function that we use for forEach
is synchronous .
What can you do about it?
We can define an anonymous function as asynchronous:
async function processArray(array) { array.forEach(async (item) => { await func(item); }) console.log('Done!'); }
But forEach
will not wait for the task to complete. forEach
is a synchronous operation. It will simply start the task and go on. Check on a simple test:
function delay() { return new Promise(resolve => setTimeout(resolve, 300)); } async function delayedLog(item) { // await Promise // delay await delay(); console.log(item); } async function processArray(array) { array.forEach(async (item) => { await delayedLog(item); }) console.log('Done!'); } processArray([1, 2, 3]);
In the console we will see:
Done! 1 2 3
In some situations this may be a normal result. But still, in most variants, this is not the appropriate logic.
To wait for the result of the execution of the loop body, we need to return to the good old "for" loop. But this time we will use its new version with the for..of
construction (thanks to Iteration Protocol ):
async function processArray(array) { for (const item of array) { await delayedLog(item); } console.log('Done!'); }
This will give us the expected result:
1 2 3 Done!
Each array element will be processed sequentially. But we can start the cycle in parallel!
You need to slightly change the code to start operations in parallel:
async function processArray(array) { // "map" const promises = array.map(delayedLog); // await Promise.all(promises); console.log('Done!'); }
This code can run several delayLog
tasks in parallel. But be careful with large arrays. Too many tasks may be too hard for the CPU and memory.
Also, please do not confuse the "parallel tasks" from the example with real parallelism and threads. This code does not guarantee parallel execution. Everything depends on the loop body (in the example, this is delayedLog
). Network requests, webworkers and some other tasks can be executed in parallel.
Source: https://habr.com/ru/post/435084/
All Articles