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

Javascript 完成所有异步forEach回调后的回调,javascript,node.js,asynchronous,callback,Javascript,Node.js,Asynchronous,Callback,正如标题所示。我该怎么做 我想在forEach循环遍历每个元素并完成一些异步处理之后调用whenaldone() [1, 2, 3].forEach( function(item, index, array, done) { asyncFunction(item, function itemDone() { console.log(item + " done"); done(); }); }, function allDone() {

正如标题所示。我该怎么做

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

[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!

迭代列表时不需要回调。只需在循环之后添加
end()
调用

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

Array.forEach
不提供这种精确性(如果可以的话),但有几种方法可以实现您想要的:

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

使用ES6承诺 (promise库可用于较旧的浏览器):

  • 处理所有保证同步执行的请求(例如1、2、3)

  • 在不执行“同步”的情况下处理所有异步请求(2可能比1更快完成)

  • 使用异步库 还有其他最流行的异步库,它们提供了表达所需内容的机制

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

    array.forEach
    res.write
    ,因此您只需在调用forEach之后放置回调:

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

    如果您遇到异步函数,并且希望确保在执行代码之前完成其任务,那么我们始终可以使用回调功能

    例如:

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

    注意:
    functionAfterForEach
    是foreach任务完成后要执行的函数
    asynchronous
    是在foreach内部执行的异步函数。

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

    使用异步npm包


    希望这能解决您的问题,我通常在需要使用内部异步任务执行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
          } 
    }
    


    奇怪的是,异步情况下给出了多少不正确的答案! 可以简单地看出,检查索引并不能提供预期的行为:

    // 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
    
    如果我们检查
    索引===array.length-1
    ,则在第一次迭代完成时将调用回调,而第一个元素仍处于挂起状态

    要解决这个问题而不使用外部库(如async),我认为最好的办法是节省列表的长度,并在每次迭代后减少if。因为只有一个线程,所以我们确信不可能出现比赛状态

    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);
    });
    
    我的解决方案:

    //对象forEachDone
    Object.defineProperty(Array.prototype,“forEachDone”{
    可枚举:false,
    值:功能(任务,cb){
    var计数器=0;
    this.forEach(函数(项、索引、数组){
    任务(项、索引、数组);
    if(array.length===++计数器){
    if(cb)cb();
    }
    });
    }
    });
    //阵列预测
    Object.defineProperty(Object.prototype,“forEachDone”{
    可枚举:false,
    值:功能(任务,cb){
    var obj=这个;
    var计数器=0;
    Object.keys(obj.forEach)(函数(键、索引、数组){
    任务(obj[key],key,obj);
    if(array.length===++计数器){
    if(cb)cb();
    }
    });
    }
    });
    
    例如:

    var-arr=['a','b','c'];
    arr.forEachDone(功能(项目){
    控制台日志(项目);
    },函数(){
    console.log('done');
    });
    //输出:a b c完成
    
    检查完整迭代计数的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);
    

    我的解决方案没有承诺(这确保了每一个行动都在下一个行动开始之前结束):

    Array.prototype.forEachAsync=函数(回调,结束){
    var self=这个;
    功能任务(索引){
    var x=自[指数];
    如果(索引>=自身长度){
    完()
    }
    否则{
    回调(self[index],索引,self,函数(){
    任务(索引+1);
    });
    }
    }
    任务(0);
    };
    var i=0;
    var myArray=Array.apply(null,数组(10)).map(函数(项){return i++;});
    log(JSON.stringify(myArray));
    forEachAsync(函数(项、索引、arr、下一步){
    setTimeout(函数(){
    $(“.toto”).append(“项目索引”+项目+“完成”);
    控制台日志(“操作”+项目+“完成”);
    next();
    }, 300);
    },函数(){
    $(“.toto”).append(“所有操作均已完成”);
    日志(“所有操作都已完成”);
    });
    
    我尝试了一种简单的解决方法,与您分享:

    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库的函数。这可以替换您想要的每个函数或代码。
    GoodLuck

    使用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');
    }
    

    但是,请注意,如果forEach中存在异步内容(例如,您正在通过URL数组进行循环并对其执行HTTP GET),则不能保证res.end将在最后调用。为了在循环中执行异步操作后触发回调,可以使用异步实用程序的each方法:为什么不使用
    if(index==array.length-1)
    并删除
    项处理
    @AminJafari,因为异步调用可能无法按照它们被调用的确切顺序解析
    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);
    });
    
    _.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);
    
    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();
                        }
                    });
    
    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)
    })
    
    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');
    }
    
     var counter = 0;
     var listArray = [0, 1, 2, 3, 4];
     function callBack() {
         if (listArray.length === counter) {
             console.log('All Done')
         }
     };
     listArray.forEach(function(element){
         console.log(element);
         counter = counter + 1;
         callBack();
     });