Callback after all asynchronous forEach callbacks are completed

后端 未结 13 2053
谎友^
谎友^ 2020-11-22 12:56

As the title suggests. How do I do this?

I want to call whenAllDone() after the forEach-loop has gone through each element and done some asynchronous p

相关标签:
13条回答
  • 2020-11-22 13:20

    If you encounter asynchronous functions, and you want to make sure that before executing the code it finishes its task, we can always use the callback capability.

    For example:

    var ctr = 0;
    posts.forEach(function(element, index, array){
        asynchronous(function(data){
             ctr++; 
             if (ctr === array.length) {
                 functionAfterForEach();
             }
        })
    });
    

    Note: functionAfterForEach is the function to be executed after foreach tasks are finished. asynchronous is the asynchronous function executed inside foreach.

    0 讨论(0)
  • 2020-11-22 13:21

    My solution:

    //Object forEachDone
    
    Object.defineProperty(Array.prototype, "forEachDone", {
        enumerable: false,
        value: function(task, cb){
            var counter = 0;
            this.forEach(function(item, index, array){
                task(item, index, array);
                if(array.length === ++counter){
                    if(cb) cb();
                }
            });
        }
    });
    
    
    //Array forEachDone
    
    Object.defineProperty(Object.prototype, "forEachDone", {
        enumerable: false,
        value: function(task, cb){
            var obj = this;
            var counter = 0;
            Object.keys(obj).forEach(function(key, index, array){
                task(obj[key], key, obj);
                if(array.length === ++counter){
                    if(cb) cb();
                }
            });
        }
    });
    

    Example:

    var arr = ['a', 'b', 'c'];
    
    arr.forEachDone(function(item){
        console.log(item);
    }, function(){
       console.log('done');
    });
    
    // out: a b c done
    
    0 讨论(0)
  • 2020-11-22 13:24

    How about setInterval, to check for complete iteration count, brings guarantee. not sure if it won't overload the scope though but I use it and seems to be the one

    _.forEach(actual_JSON, function (key, value) {
    
         // run any action and push with each iteration 
    
         array.push(response.id)
    
    });
    
    
    setInterval(function(){
    
        if(array.length > 300) {
    
            callback()
    
        }
    
    }, 100);
    
    0 讨论(0)
  • 2020-11-22 13:25

    You shouldn't need a callback for iterating through a list. Just add the end() call after the loop.

    posts.forEach(function(v, i){
       res.write(v + ". Index " + i);
    });
    res.end();
    
    0 讨论(0)
  • 2020-11-22 13:28

    Hope this will fix your problem, i usually work with this when i need to execute forEach with asynchronous tasks inside.

    foo = [a,b,c,d];
    waiting = foo.length;
    foo.forEach(function(entry){
          doAsynchronousFunction(entry,finish) //call finish after each entry
    }
    function finish(){
          waiting--;
          if (waiting==0) {
              //do your Job intended to be done after forEach is completed
          } 
    }
    

    with

    function doAsynchronousFunction(entry,callback){
           //asynchronousjob with entry
           callback();
    }
    
    0 讨论(0)
  • 2020-11-22 13:30

    Array.forEach does not provide this nicety (oh if it would) but there are several ways to accomplish what you want:

    Using a simple counter

    function callback () { console.log('all done'); }
    
    var itemsProcessed = 0;
    
    [1, 2, 3].forEach((item, index, array) => {
      asyncFunction(item, () => {
        itemsProcessed++;
        if(itemsProcessed === array.length) {
          callback();
        }
      });
    });
    

    (thanks to @vanuan and others) This approach guarantees that all items are processed before invoking the "done" callback. You need to use a counter that gets updated in the callback. Depending on the value of the index parameter does not provide the same guarantee, because the order of return of the asynchronous operations is not guaranteed.

    Using ES6 Promises

    (a promise library can be used for older browsers):

    1. Process all requests guaranteeing synchronous execution (e.g. 1 then 2 then 3)

      function asyncFunction (item, cb) {
        setTimeout(() => {
          console.log('done with', item);
          cb();
        }, 100);
      }
      
      let requests = [1, 2, 3].reduce((promiseChain, item) => {
          return promiseChain.then(() => new Promise((resolve) => {
            asyncFunction(item, resolve);
          }));
      }, Promise.resolve());
      
      requests.then(() => console.log('done'))
      
    2. Process all async requests without "synchronous" execution (2 may finish faster than 1)

      let requests = [1,2,3].map((item) => {
          return new Promise((resolve) => {
            asyncFunction(item, resolve);
          });
      })
      
      Promise.all(requests).then(() => console.log('done'));
      

    Using an async library

    There are other asynchronous libraries, async being the most popular, that provide mechanisms to express what you want.

    Edit

    The body of the question has been edited to remove the previously synchronous example code, so i've updated my answer to clarify. The original example used synchronous like code to model asynchronous behaviour, so the following applied:

    array.forEach is synchronous and so is res.write, so you can simply put your callback after your call to foreach:

      posts.foreach(function(v, i) {
        res.write(v + ". index " + i);
      });
    
      res.end();
    
    0 讨论(0)
提交回复
热议问题