Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return many Promises and wait for them all before doing other stuff

I have a loop which calls a method that does stuff asynchronously. This loop can call the method many times. After this loop, I have another loop that needs to be executed only when all the asynchronous stuff is done.

So this illustrates what I want:

for (i = 0; i < 5; i++) {     doSomeAsyncStuff();     }  for (i = 0; i < 5; i++) {     doSomeStuffOnlyWhenTheAsyncStuffIsFinish();     } 

I'm not very familiar with promises, so could anyone help me to achieve this?

This is how my doSomeAsyncStuff() behaves:

function doSomeAsyncStuff() {     var editor = generateCKEditor();     editor.on('instanceReady', function(evt) {         doSomeStuff();         // There should be the resolve() of the promises I think.     }) } 

Maybe I have to do something like this:

function doSomeAsyncStuff() {     var editor = generateCKEditor();     return new Promise(function(resolve,refuse) {         editor.on('instanceReady', function(evt) {             doSomeStuff();             resolve(true);         });     }); } 

But I'm not sure of the syntax.

like image 895
Ganbin Avatar asked Jul 15 '15 09:07

Ganbin


People also ask

How do you wait for promises to complete?

allSettled() will wait for all input promises to complete, regardless of whether or not one rejects. Consequently, it will always return the final result of every promise and function from the input iterable. Note: The order of the promise array is preserved upon completion of this method.

How will you wait for all the promises to resolve reject that are executed inside a loop?

You can use Promise. all (spec, MDN) for that: It accepts a bunch of individual promises and gives you back a single promise that is resolved when all of the ones you gave it are resolved, or rejected when any of them is rejected.

Do promises always return promises?

There are no code paths where you instead decided you're going to return a value so that new Promise() should just return that value instead of returning a new promise. So, as most would expect new Promise() always returns a promise, regardless of what you do in the promise executor callback function.

Why do we need promises all?

Promise. all() is useful anytime you have more than one promise and your code wants to know when all the operations that those promises represent have finished successfully. It does not matter what the individual async operations are.


2 Answers

You can use Promise.all (spec, MDN) for that: It accepts a bunch of individual promises and gives you back a single promise that is resolved when all of the ones you gave it are resolved, or rejected when any of them is rejected.

So if you make doSomeAsyncStuff return a promise, then:

    const promises = []; //  ^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− use `const` or `let`, not `var`          for (let i = 0; i < 5; i++) { //       ^^^−−−−−−−−−−−−−−−−−−−−−−−− added missing declaration         promises.push(doSomeAsyncStuff());     }          Promise.all(promises)         .then(() => {             for (let i = 0; i < 5; i++) { //               ^^^−−−−−−−−−−−−−−−− added missing declaration                 doSomeStuffOnlyWhenTheAsyncStuffIsFinish();                 }         })         .catch((e) => {             // handle errors here         }); 

MDN has an article on promises here. I also cover promsies in detail in Chapter 8 of my book JavaScript: The New Toys, links in my profile if you're interested.

Here's an example:

 function doSomethingAsync(value) {      return new Promise((resolve) => {          setTimeout(() => {              console.log("Resolving " + value);              resolve(value);          }, Math.floor(Math.random() * 1000));      });    }        function test() {        const promises = [];                for (let i = 0; i < 5; ++i) {            promises.push(doSomethingAsync(i));        }                Promise.all(promises)            .then((results) => {                console.log("All done", results);            })            .catch((e) => {                // Handle errors here            });    }        test();

Sample output (because of the Math.random, what finishes first may vary):

 Resolving 3 Resolving 2 Resolving 1 Resolving 4 Resolving 0 All done [0,1,2,3,4] 
like image 129
T.J. Crowder Avatar answered Oct 01 '22 03:10

T.J. Crowder


A reusable function works nicely for this pattern:

function awaitAll(count, asyncFn) {   const promises = [];    for (i = 0; i < count; ++i) {     promises.push(asyncFn());   }    return Promise.all(promises); } 

OP example:

awaitAll(5, doSomeAsyncStuff)   .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))   .catch(e => console.error(e)); 

A related pattern, is iterating over an array and performing an async operation on each item:

function awaitAll(list, asyncFn) {   const promises = [];    list.forEach(x => {     promises.push(asyncFn(x));   });    return Promise.all(promises); } 

Example:

const books = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }];  function doSomeAsyncStuffWith(book) {   return Promise.resolve(book.name); }  awaitAll(books, doSomeAsyncStuffWith)   .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))   .catch(e => console.error(e)); 
like image 20
2Toad Avatar answered Oct 01 '22 01:10

2Toad