首页 文章

完成所有异步forEach回调后的回调

提问于
浏览
177

正如 Headers 所示 . 我该怎么做呢?

在forEach循环遍历每个元素并完成一些异步处理之后,我想调用 whenAllDone() .

[1, 2, 3].forEach(
  function(item, index, array, done) {
     asyncFunction(item, function itemDone() {
       console.log(item + " done");
       done();
     });
  }, function allDone() {
     console.log("All done");
     whenAllDone();
  }
);

有可能让它像这样工作吗?当forEach的第二个参数是一个回调函数,它在经过所有迭代后运行?

预期产量:

3 done
1 done
2 done
All done!

13 回答

  • 0

    Array.forEach 没有提供这种精确性(哦,如果它会)但有几种方法可以实现你想要的:

    使用简单的计数器

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

    (感谢@vanuan和其他人)这种方法保证在调用“完成”回调之前处理所有项目 . 您需要使用在回调中更新的计数器 . 取决于索引参数的值不提供相同的保证,因为不保证异步操作的返回顺序 .

    使用ES6承诺

    (promise库可用于旧版浏览器):

    • 处理保证同步执行的所有请求(例如1然后2然后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可能比1更快完成)
    let requests = [1,2,3].map((item) => {
        return new Promise((resolve) => {
          asyncFunction(item, resolve);
        });
    })
    
    Promise.all(requests).then(() => console.log('done'));
    

    使用异步库

    还有其他异步库,async是最受欢迎的,提供表达您想要的机制 .
    编辑

    问题的主体已被编辑,以删除以前的同步示例代码,所以我已更新我的答案澄清 . 原始示例使用同步代码来模拟异步行为,因此应用了以下内容:

    array.forEachsynchronous,因此是 res.write ,因此您可以在调用foreach后简单地进行回调:

    posts.foreach(function(v, i) {
        res.write(v + ". index " + i);
      });
    
      res.end();
    
  • -2

    如果你遇到异步函数,并且你想确保在执行代码之前完成它的任务,我们总是可以使用回调功能 .

    例如:

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

    注意:functionAfterForEach是在foreach任务完成后执行的函数 . asynchronous是在foreach中执行的异步函数 .

    希望这可以帮助 .

  • 21

    很奇怪有多少错误答案给了异步案例!可以简单地显示检查索引不提供预期的行为:

    // INCORRECT
    var list = [4000, 2000];
    list.forEach(function(l, index) {
        console.log(l + ' started ...');
        setTimeout(function() {
            console.log(index + ': ' + l);
        }, l);
    });
    

    输出:

    4000 started
    2000 started
    1: 2000
    0: 4000
    

    如果我们检查 index === array.length - 1 ,将在完成第一次迭代时调用回调,而第一个元素仍处于未决状态!

    为了在不使用异步等外部库的情况下解决这个问题,我认为最好的办法是保存列表的长度,并在每次迭代后减少 . 由于只有一个线程,我们确信没有竞争条件的可能性 .

    var list = [4000, 2000];
    var counter = list.length;
    list.forEach(function(l, index) {
        console.log(l + ' started ...');
        setTimeout(function() {
            console.log(index + ': ' + l);
            counter -= 1;
            if ( counter === 0)
                // call your callback here
        }, l);
    });
    
  • 0

    希望这能解决你的问题,当我需要执行forEach内部的异步任务时,我通常会使用它 .

    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
          } 
    }
    

    function doAsynchronousFunction(entry,callback){
           //asynchronousjob with entry
           callback();
    }
    
  • 1

    我的解决方案没有Promise(这确保每个操作在下一个操作开始之前结束):

    Array.prototype.forEachAsync = function (callback, end) {
            var self = this;
        
            function task(index) {
                var x = self[index];
                if (index >= self.length) {
                    end()
                }
                else {
                    callback(self[index], index, self, function () {
                        task(index + 1);
                    });
                }
            }
        
            task(0);
        };
        
        
        var i = 0;
        var myArray = Array.apply(null, Array(10)).map(function(item) { return i++; });
        console.log(JSON.stringify(myArray));
        myArray.forEachAsync(function(item, index, arr, next){
          setTimeout(function(){
            $(".toto").append("<div>item index " + item + " done</div>");
            console.log("action " + item + " done");
            next();
          }, 300);
        }, function(){
            $(".toto").append("<div>ALL ACTIONS ARE DONE</div>");
            console.log("ALL ACTIONS ARE DONE");
        });
    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="toto">
    
    </div>
    
  • 307

    使用ES2018,您可以使用异步迭代器:

    const asyncFunction = a => fetch(a);
    const itemDone = a => console.log(a);
    
    async function example() {
      const arrayOfFetchPromises = [1, 2, 3].map(asyncFunction);
    
      for await (const item of arrayOfFetchPromises) {
        itemDone(item);
      }
    
      console.log('All done');
    }
    
  • 9

    这是Node.js的解决方案,它是异步的 .

    使用async npm包 .

    (JavaScript) Synchronizing forEach Loop with callbacks inside

  • -1

    我的解决方案

    //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();
                }
            });
        }
    });
    

    例:

    var arr = ['a', 'b', 'c'];
    
    arr.forEachDone(function(item){
        console.log(item);
    }, function(){
       console.log('done');
    });
    
    // out: a b c done
    
  • -1

    我尝试使用Easy Way解决它,与您分享:

    let counter = 0;
                arr.forEach(async (item, index) => {
                    await request.query(item, (err, recordset) => {
                        if (err) console.log(err);
    
                        //do Somthings
    
                        counter++;
                        if(counter == tableCmd.length){
                            sql.close();
                            callback();
                        }
                    });
    

    request 是节点js中mssql库的功能 . 这可以替换您想要的每个功能或代码 . 祝好运

  • 2
    var i=0;
    const waitFor = (ms) => 
    { 
      new Promise((r) => 
      {
       setTimeout(function () {
       console.log('timeout completed: ',ms,' : ',i); 
         i++;
         if(i==data.length){
          console.log('Done')  
        }
      }, ms); 
     })
    }
    var data=[1000, 200, 500];
    data.forEach((num) => {
      waitFor(num)
    })
    
  • 0

    setInterval如何检查完整的迭代次数,带来保证 . 不确定它是否会超载范围,但我使用它,似乎是一个

    _.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

    一个简单的解决方案就像跟随

    function callback(){console.log("i am done");}
    
    ["a", "b", "c"].forEach(function(item, index, array){
        //code here
        if(i == array.length -1)
        callback()
    }
    
  • 13

    您不需要回调来遍历列表 . 只需在循环后添加 end() 调用即可 .

    posts.forEach(function(v, i){
       res.write(v + ". Index " + i);
    });
    res.end();
    

相关问题