Обратный вызов после завершения всех асинхронных обратных вызовов forEach
как следует из названия. Как мне это сделать?
Я хочу позвонить 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();
}
);
можно заставить его работать? Когда второй аргумент forEach является функцией обратного вызова, которая выполняется после того, как она прошла все итерации?
ожидаемый результат:
3 done
1 done
2 done
All done!
13 ответов:
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 и другим) этот подход гарантирует, что все элементы обрабатываются перед вызовом обратного вызова "done". Подход, который предлагает Эмиль, хотя он обычно эффективен в моем опыте, не дает такой же гарантии.
использование ES6 обещает
(библиотека обещаю может использоваться для старых браузеров):
обрабатывать все запросы, гарантирующие синхронное выполнение (например, 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'));использование асинхронной библиотеки
есть и другие асинхронные библиотеки, асинхронные будучи самым популярным, которые обеспечивают механизмы, чтобы выразить то, что вы хотите.
Редактировать
тело вопроса было отредактировано, чтобы удалить ранее синхронный пример кода, поэтому я обновил свой ответ, чтобы уточнить. В исходном примере использовался синхронный подобный код для моделирования асинхронного поведения, поэтому применялось следующее:
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. асинхронная-это асинхронная функция, выполняемая внутри foreach.
надеюсь, что это помогает.
странно, сколько неправильных ответов было дано асинхронные случае! Можно просто показать, что проверка индекса не обеспечивает ожидаемого поведения:
// 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); });
надеюсь, что это исправит вашу проблему, я обычно работаю с этим, когда мне нужно выполнить 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(); }
мое решение без обещания (это гарантирует, что каждое действие заканчивается до начала следующего):
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>
С 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'); }
Это решение для узла.js, который является асинхронным.
использование асинхронного пакета npm.
(JavaScript) синхронизация цикла forEach с обратными вызовами внутри
Как насчет 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);
мое решение:
//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
Я пытаюсь простой способ решить эту проблему, поделиться им с вами:
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 в узле js. Это может заменить каждую функцию или код вы хотите. Гудлак
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) })
простое решение было бы похоже на follow
function callback(){console.log("i am done");} ["a", "b", "c"].forEach(function(item, index, array){ //code here if(i == array.length -1) callback() }
вы не должны нуждаться в обратном вызове для итерации по списку. Просто добавьте
end()звонить после цикла.posts.forEach(function(v, i){ res.write(v + ". Index " + i); }); res.end();
Comments