等待. forEach()完成的最佳方式

有时我需要等待 .forEach()方法完成,主要是在“加载程序”函数上。我是这么做的:

$q.when(array.forEach(function(item){
//iterate on something
})).then(function(){
//continue with processing
});

我不禁觉得这不是等待 .forEach()结束的最佳方式。最好的方法是什么?

153810 次浏览

如果在 forEach中没有 异步的代码,那么 forEach就不是异步的,例如在这段代码中:

array.forEach(function(item){
//iterate on something
});
alert("Foreach DONE !");

你会看到警报后,forEach完成。

否则(您的内部有一些异步的东西) ,您可以将 forEach循环包装在 我保证中:

var bar = new Promise((resolve, reject) => {
foo.forEach((value, index, array) => {
console.log(value);
if (index === array.length -1) resolve();
});
});


bar.then(() => {
console.log('All done!');
});

图片来源:@rolando-Benjamin-vaz-ferreira

var foo = [1,2,3,4,5,6,7,8,9,10];

如果您实际上在循环中执行异步操作,那么您可以将其包装在一个承诺中..。

var bar = new Promise((resolve, reject) => {
foo.forEach((value, index, array) => {
console.log(value);
if (index === array.length -1) resolve();
});
});


bar.then(() => {
console.log('All done!');
});

使用 ES6的最快方法是使用 for..of循环。

const myAsyncLoopFunction = async (array) => {
const allAsyncResults = []


for (const item of array) {
const asyncResult = await asyncFunction(item)
allAsyncResults.push(asyncResult)
}


return allAsyncResults
}

或者您可以使用 Promise.all()并行地遍历所有这些异步请求,如下所示:

const myAsyncLoopFunction = async (array) => {
const promises = array.map(asyncFunction)
await Promise.all(promises)
console.log(`All async tasks complete!`)
}

forEach ()不返回任何内容,因此更好的做法是 map() + Promise.all()

var arr = [1, 2, 3, 4, 5, 6]


var doublify = (ele) => {
return new Promise((res, rej) => {
setTimeout(() => {
res(ele * 2)
}, Math.random() ); // Math.random returns a random number from 0~1
})
}


var promises = arr.map(async (ele) => {
// do some operation on ele
// ex: var result = await some_async_function_that_return_a_promise(ele)
// In the below I use doublify() to be such an async function


var result = await doublify(ele)
return new Promise((res, rej) => {res(result)})
})


Promise.all(promises)
.then((results) => {
// do what you want on the results
console.log(results)
})

output

与其他版本相比,我不确定这个版本的效率如何,但是最近我在 forEach ()内部有一个异步函数时使用了这个版本。它不使用承诺、映射或 for-of 循环:

// n'th triangular number recursion (aka factorial addition)
function triangularNumber(n) {
if (n <= 1) {
return n
} else {
return n + triangularNumber(n-1)
}
}


// Example function that waits for each forEach() iteraction to complete
function testFunction() {
// Example array with values 0 to USER_INPUT
var USER_INPUT = 100;
var EXAMPLE_ARRAY = Array.apply(null, {length: USER_INPUT}).map(Number.call, Number) // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, n_final... ] where n_final = USER_INPUT-1


// Actual function used with whatever actual array you have
var arrayLength = EXAMPLE_ARRAY.length
var countMax = triangularNumber(arrayLength);
var counter = 0;
EXAMPLE_ARRAY.forEach(function(entry, index) {
console.log(index+1); // show index for example (which can sometimes return asynchrounous results)


counter += 1;
if (triangularNumber(counter) == countMax) {


// function called after forEach() is complete here
completionFunction();
} else {
// example just to print counting values when max not reached
// else would typically be excluded
console.log("Counter index: "+counter);
console.log("Count value: "+triangularNumber(counter));
console.log("Count max: "+countMax);
}
});
}
testFunction();


function completionFunction() {
console.log("COUNT MAX REACHED");
}

我必须处理同样的问题(为每个人使用 里面有很多承诺) ,目前提出的解决方案没有一个对我有帮助。因此,我实现了一个检查数组,每个承诺更新其完整的状态。我们有一个总的承诺,包装过程。我们只有在每一个承诺完成时才解决一般的承诺。代码片段:

function WaitForEachToResolve(fields){


var checked_fields = new Array(fields.length).fill(0);
const reducer = (accumulator, currentValue) => accumulator + currentValue;


return new Promise((resolve, reject) => {


Object.keys(fields).forEach((key, index, array) => {


SomeAsyncFunc(key)
.then((result) => {


// some result post process


checked_fields[index] = 1;
if (checked_fields.reduce(reducer) === checked_fields.length)
resolve();
})
.catch((err) => {
reject(err);
});
}
)}
}

我喜欢使用 异步-等待而不是. then ()语法,所以对于 数据的异步处理,这样修改了@Ronaldo 的答案-

let finalData = [];
var bar = new Promise(resolve => {
foo.forEach((value, index) => {
const dataToGet = await abcService.getXyzData(value);
finalData[index].someKey = dataToGet.thatOtherKey;
// any other processing here
if (finalData[dataToGet.length - 1].someKey) resolve();
});
});


await Promise.all([bar]);
console.log(`finalData: ${finalData}`);

注意 : < em > 我已经修改了 if 条件,它解决了满足条件的承诺。你也可以这么做。

在每个可能的唯一代码分支的末尾修改和检查一个计数器,包括回调。示例:

const fs = require('fs');


/**
* @description Delete files older than 1 day
* @param {String} directory - The directory to purge
* @return {Promise}
*/
async function purgeFiles(directory) {
const maxAge = 24*3600000;
const now = Date.now();
const cutoff = now-maxAge;


let filesPurged = 0;
let filesProcessed = 0;
let purgedSize = 0;


await new Promise( (resolve, reject) => {
fs.readdir(directory, (err, files) => {
if (err) {
return reject(err);
}
if (!files.length) {
return resolve();
}
files.forEach( file => {
const path = `${directory}/${file}`;
fs.stat(path, (err, stats)=> {
if (err) {
console.log(err);
if (++filesProcessed === files.length) resolve();
}
else if (stats.isFile() && stats.birthtimeMs < cutoff) {
const ageSeconds = parseInt((now-stats.birthtimeMs)/1000);
fs.unlink(path, error => {
if (error) {
console.log(`Deleting file failed: ${path} ${error}`);
}
else {
++filesPurged;
purgedSize += stats.size;
console.log(`Deleted file with age ${ageSeconds} seconds: ${path}`);
}
if (++filesProcessed === files.length) resolve();
});
}
else if (++filesProcessed === files.length) resolve();
});
});
});
});


console.log(JSON.stringify({
directory,
filesProcessed,
filesPurged,
purgedSize,
}));
}


// !!DANGER!! Change this line! (intentional syntax error in ,')
const directory = ,'/tmp'; // !!DANGER!! Changeme
purgeFiles(directory).catch(error=>console.log(error));

一个通用的解决方案,用于确保所有 forEach ()元素都完成了执行。

const testArray = [1,2,3,4]
let count = 0


await new Promise( (resolve) => {
testArray.forEach( (num) => {
try {
//some real logic
num = num * 2
} catch (e) {
// error handling
console.log(e)
} fanally {
// most important is here
count += 1
if (count == testArray.length) {
resolve()
}
}
})
})

这个想法与使用索引来计数的答案是一样的。但在实际情况中,如果发生错误,索引方法不能正确计数。所以解决方案更加稳健。

谢谢

const array = [1, 2, 3];
const results = [];
let done = 0;


const asyncFunction = (item, callback) =>
setTimeout(() => callback(item * 10), 100 - item * 10);


new Promise((resolve, reject) => {
array.forEach((item) => {
asyncFunction(item, (result) => {
results.push(result);
done++;
if (done === array.length) resolve();
});
});
}).then(() => {
console.log(results); // [30, 20, 10]
});


// or
// promise = new Promise(...);
// ...
// promise.then(...);

“ result”数组中的结果顺序可能不同于原始数组中项目的顺序,这取决于每个项目的异步函数()完成的时间。

您可以使用它,因为我们在 forEach循环中使用的是异步/等待。您可以在循环中使用自己的逻辑。

    let bar = new Promise((resolve, reject) => {
snapshot.forEach(async (doc) => {
"""Write your own custom logic and can use async/await
"""
const result = await something()
resolve(result);
});
});
let test = []
test.push(bar)
let concepts = await Promise.all(test);
console.log(concepts);

如果你在一个循环中有一个异步任务,你想等待。你可以使用 for await

for await (const i of images) {
let img = await uploadDoc(i);
};


let x = 10; //this executes after

对于简单的比较代码,我喜欢用于语句。

doit();
function doit() {


for (var i = 0; i < $('span').length;  i++) {
console.log(i,$('span').eq(i).text() );
if ( $('span').eq(i).text() == "Share a link to this question"  ) { //  span number 59
return;
}
}


alert('never execute');


}

使用 for of代替 forEach,像这样:

for (const item of array) {
//do something
}
console.log("finished");

finished”将被记录后,完成循环。

我一直在使用这个,它工程最好的 .forEach()

//count
var expecting = myArray.length;


myArray.forEach(function(item){


//do logic here
var item = item






//when iteration done
if (--expecting === 0) {


console.log('all done!');


}


})

如果在 for循环中存在异步(可观察的)方法调用,您可以使用以下方法:

await players.reduce(async (a, player) => {
// Wait for the previous item to finish processing
await a;
// Process this item
await givePrizeToPlayer(player);
}, Promise.resolve());

检查这里: https://gist.github.com/joeytwiddle/37d2085425c049629b80956d3c618971