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

正如标题所示。我怎么做呢?

我想在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!
383784 次浏览

你不应该需要一个回调来迭代一个列表。只需在循环之后添加end()调用。

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

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

这种方法保证在调用"done"回调之前处理所有项。您需要使用一个在回调中更新的计数器。依赖于索引参数的值并不能提供相同的保证,因为异步操作的返回顺序是不能保证的。

使用ES6承诺

(一个承诺库可以用于较旧的浏览器):

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

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

例如:

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包。

(JavaScript)同步forEach循环与回调内部

希望这将解决你的问题,我通常与此工作时,我需要执行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();
}
异步的例子有这么多错误的答案,这很奇怪! 可以简单地表明,检查索引没有提供预期的行为:

// 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,回调将在第一次迭代完成时被调用,而第一个元素仍然挂起!

要在不使用async等外部库的情况下解决这个问题,我认为最好的方法是保存列表的长度,并在每次迭代后递减。因为只有一个线程,我们确定没有竞争条件的机会。

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

我的解决方案:

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

如果使用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 = 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>

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

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是mssql库在Node js中的函数。这可以取代你想要的每个函数或代码。 古德勒克< / p >

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

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

在这个帖子中有许多解决方案和方法来实现这一点!

但是,如果你需要用地图异步/等待这样做,那么它就在这里

// Execution Starts
console.log("start")


// The Map will return promises
// the Execution will not go forward until all the promises are resolved.
await Promise.all(
[1, 2, 3].map( async (item) => {
await asyncFunction(item)
})
)


// Will only run after all the items have resolved the asynchronous function.
console.log("End")

输出将如下所示!可能根据异步函数而有所不同。

start
2
3
1
end

请注意:如果你在映射中使用等待,它将总是返回promises数组。

//First Example
save() {
this.holdImageData.forEach((val,i) => {
this.UploadToMinio(val.file, val.index, res => {
if (res && i+1 == this.holdImageData.length) {
this.FinallySave();
}
})
})
}


UploadToMinio(files, index, callback) {
this._fileUploadService.uploadFile(files[0], files[0].name, 'rms').subscribe(data => {
if (data) {
console.log('data >>> ', data);
callback(true);
}
})
}


FinallySave() {}


//Second Example
var sum = 0; // It can be global variable
startFunction() {
this.sumFunction(2, 4, res => {
if (res == true && sum == 6) {
this.saveFunction();
} else {
//call to another function
}
})
}


sumFunction(num1, num2, callback) {
if ((num1 + num2) == 6) {
callback(true)
}
callback(false);
}