I have a loop that calls several sync functions simultaneously and in parallel. When they are all done, a series of final operations must be called to end the program.
For example, look at this simple code:
(async () => { var items = [1, 2, 3, 4, 5]; for (var item of items) { console.log('Running... #' + item); var loop = async function(item) { var delay = getRandom(3, 7)*1000; await sleep(delay); console.log('Done. #' + item + ' with delay: ' + delay); } loop(item); } //while (true) {await sleep(1000);} console.log('Finish'); })(); /////////////////////////////////////// async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function getRandom(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1) + min); } The output is as follows:
Running... #1 Running... #2 Running... #3 Running... #4 Running... #5 Finish Done. #5 with delay: 3000 Done. #2 with delay: 4000 Done. #4 with delay: 6000 Done. #1 with delay: 7000 Done. #3 with delay: 7000 Which is not good, because "Finish" is printed before the end of all the loops.
Note that "Finish" is an example, and here I may have final operations such as closing a database or browser, and so on.
If I call the functions like this:
await loop(item); Result:
Running... #1 Done. #1 with delay: 3000 Running... #2 Done. #2 with delay: 7000 Running... #3 Done. #3 with delay: 6000 Running... #4 Done. #4 with delay: 6000 Running... #5 Done. #5 with delay: 5000 Finish The phrase "Finish" is printed well at the end, but instead all my functions are executed async and in order, which is not desirable.
Another way I tried is to use an infinite While similar to the one I commented on in the source code:
while (true) {await sleep(1000);} Result:
Running... #1 Running... #2 Running... #3 Running... #4 Running... #5 Done. #1 with delay: 3000 Done. #5 with delay: 4000 Done. #3 with delay: 6000 Done. #4 with delay: 6000 Done. #2 with delay: 7000 The operation is correct here, but it never reaches "Finish".
Another way I thought of was to put a counter at the end of the functions and put a condition inside the end loop to control it and exit the loop.
like this:
(async () => { var items = [1, 2, 3, 4, 5]; var n = 0; // <==== for (var item of items) { console.log('Running... #' + item); var loop = async function(item) { var delay = getRandom(3, 7)*1000; await sleep(delay); console.log('Done. #' + item + ' with delay: ' + delay); n++; // <==== } loop(item); } while (true) { await sleep(1000); if (n == items.length) break; // <==== } console.log('Finish'); })(); And its output:
Running... #1 Running... #2 Running... #3 Running... #4 Running... #5 Done. #2 with delay: 3000 Done. #1 with delay: 4000 Done. #5 with delay: 4000 Done. #3 with delay: 5000 Done. #4 with delay: 6000 Finish This is my expected and desired output. But I think the solution is not clean! Because the counter is difficult to control in the program, and maybe in some places I needed to return from the function and I should always be careful to increase n by one.
So I think there should be a simpler and more principled solution to achieve this goal. Maybe, for example, putting all the call results in an array and checking it in While, but as much as possible I do not want to change the structure of my program, and it is better to add everything simply and at the end of the code.
Do you have a solution?
await Promise.all(items.map(async (item) => { /*...code using `await`...*/ }));