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

后端 未结 5 1784
北荒
北荒 2020-11-21 23:02

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 o

相关标签:
5条回答
  • 2020-11-21 23:15

    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]
    
    0 讨论(0)
  • 2020-11-21 23:15
    const doSomeAsyncStuff = async (funcs) => {
      const allPromises = funcs.map(func => func());
      return await Promise.all(allPromises);
    }
    
    doSomeAsyncStuff([
      () => new Promise(resolve => setTimeout(() => resolve(), 100)),
      () => new Promise(resolve => setTimeout(() => resolve(), 100)),
      () => new Promise(resolve => setTimeout(() => resolve(), 100)),
      () => new Promise(resolve => setTimeout(() => resolve(), 100)),
      () => new Promise(resolve => setTimeout(() => resolve(), 100)),
    ]);
    
    0 讨论(0)
  • 2020-11-21 23:17

    Here is code that I wrote for myself in order to understand the answers stated here. I have mongoose queries in a for loop, so I put here the asyncFunction to take its place. Hope it helps anyone. You can run this script in node or any of many Javascript runtimes.

    let asyncFunction = function(value, callback)
    {
            setTimeout(function(){console.log(value); callback();}, 1000);
    }
    
    
    
    // a sample function run without promises
    
    asyncFunction(10,
        function()
        {
            console.log("I'm back 10");
        }
    );
    
    
    //here we use promises
    
    let promisesArray = [];
    
    let p = new Promise(function(resolve)
    {
        asyncFunction(20,
            function()
            {
                console.log("I'm back 20");
                resolve(20);
            }
        );
    });
    
    promisesArray.push(p);
    
    
    for(let i = 30; i < 80; i += 10)
    {
        let p = new Promise(function(resolve)
        {
            asyncFunction(i,
                function()
                {
                    console.log("I'm back " + i);
                    resolve(i);
                }
            );
        });
        promisesArray.push(p);
    }
    
    
    // We use Promise.all to execute code after all promises are done.
    
    Promise.all(promisesArray).then(
        function()
        {
            console.log("all promises resolved!");
        }
    )
    
    0 讨论(0)
  • 2020-11-21 23:30

    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));
    
    0 讨论(0)
  • 2020-11-21 23:36

    /*** Worst way ***/
    for(i=0;i<10000;i++){
      let data = await axios.get(
        "https://yourwebsite.com/get_my_data/"
      )
      //do the statements and operations
      //that are dependant on data
    }
    
    //Your final statements and operations
    //That will be performed when the loop ends
    
    //=> this approach will perform very slow as all the api call
    // will happen in series
    
    
    /*** One of the Best way ***/
    
    const yourAsyncFunction = async (anyParams) => {
      let data = await axios.get(
        "https://yourwebsite.com/get_my_data/"
      )
      //all you statements and operations here
      //that are dependant on data
    }
    var promises = []
    for(i=0;i<10000;i++){
      promises.push(yourAsyncFunction(i))
    }
    await Promise.all(promises)
    //Your final statement / operations
    //that will run once the loop ends
    
    //=> this approach will perform very fast as all the api call
    // will happen in parallal

    0 讨论(0)
提交回复
热议问题