node.js 所有异步 forEach 回调完成后的回调

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18983138/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:43:58  来源:igfitidea点击:

Callback after all asynchronous forEach callbacks are completed

javascriptnode.jsasynchronouscallback

提问by Dan Andreasson

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

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

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

Possible to get it to work like this? When the second argument to forEach is a callback function which runs once it went through all iterations?

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

Expected output:

预期输出:

3 done
1 done
2 done
All done!

回答by Nick Tomlin

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

Array.forEach不提供这种细节(哦,如果可以的话)但是有几种方法可以完成您想要的:

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.

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

Using ES6 Promises

使用 ES6 承诺

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

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

  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'));
    
  1. 处理所有保证同步执行的请求(例如 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. 在没有“同步”执行的情况下处理所有异步请求(2 可能比 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, asyncbeing the most popular, that provide mechanisms to express what you want.

还有其他异步库,async是最流行的,它们提供了表达您想要的内容的机制。

编辑

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.forEachis synchronousand so is res.write, so you can simply put your callback after your call to foreach:

array.forEach同步的,所以是res.write,这样你就可以简单地把你的回调您的来电后的foreach:

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

  res.end();

回答by Emil Re?a Enriquez

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: functionAfterForEachis the function to be executed after foreach tasks are finished. asynchronousis the asynchronous function executed inside foreach.

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

回答by Adnene Belfodil

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

希望这能解决您的问题,当我需要在内部执行 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
      } 
}

with

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

回答by Rsh

It's odd how many incorrect answers has been given to asynchronouscase! It can be simply shown that checking index does not provide expected behavior:

奇怪的是,异步案例有多少不正确的答案!可以简单地表明检查索引不提供预期的行为:

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

output:

输出:

4000 started
2000 started
1: 2000
0: 4000

If we check for index === array.length - 1, callback will be called upon completion of first iteration, whilst first element is still pending!

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

To solve this problem without using external libraries such as async, I think your best bet is to save length of list and decrement if after each iteration. Since there's just one thread we're sure there no chance of race condition.

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

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

回答by Krzysztof Grzybek

With ES2018 you can use async iterators:

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

回答by Hymanstrapp

My solution without Promise (this ensures that every action is ended before the next one begins):

我没有 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>

回答by Hardik Shimpi

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

回答by HamidReza Heydari

I try Easy Way to resolve it, share it with you :

我尝试简单的方法来解决它,与您分享:

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

requestis Function of mssql Library in Node js. This can replace each function or Code u want. GoodLuck

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

回答by Nilesh Pawar

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

回答by Gabor

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