Какой самый умный/самый чистый способ итерировать асинхронные массивы (или objs)?
Вот как я это делаю:
function processArray(array, index, callback) {
processItem(array[index], function(){
if(++index === array.length) {
callback();
return;
}
processArray(array, index, callback);
});
};
function processItem(item, callback) {
// do some ajax (browser) or request (node) stuff here
// when done
callback();
}
var arr = ["url1", "url2", "url3"];
processArray(arr, 0, function(){
console.log("done");
});
Это хорошо? Как избежать этого спагетти-кода?
Ответы
Ответ 1
Оформить заказ async, он сделал для потока управления (async), и у него есть много методов для массива: каждый, фильтр, карта. Проверьте документацию по github. Вот что вам, вероятно, нужно:
каждый (обр, итератор, обратный вызов)
Применяет функцию итератора к каждому элементу массива параллельно. Итератор вызывается с элементом из списка и обратным вызовом, когда он закончил. Если итератор передает ошибку этому обратному вызову, основной обратный вызов для функции each
немедленно вызывается с ошибкой.
eachSeries (arr, итератор, обратный вызов)
То же, что и each
, к каждому элементу массива последовательно применяется только итератор. Следующий итератор вызывается только после завершения обработки текущего. Это означает, что функции итератора будут завершены по порядку.
Ответ 2
Как указано в некотором ответе, можно использовать библиотеку "async". Но иногда вы просто не хотите вводить новую зависимость в свой код. И ниже - еще один способ, как вы можете зацикливаться и ждать завершения некоторых асинхронных функций.
var items = ["one", "two", "three"];
// This is your async function, which may perform call to your database or
// whatever...
function someAsyncFunc(arg, cb) {
setTimeout(function () {
cb(arg.toUpperCase());
}, 3000);
}
// cb will be called when each item from arr has been processed and all
// results are available.
function eachAsync(arr, func, cb) {
var doneCounter = 0,
results = [];
arr.forEach(function (item) {
func(item, function (res) {
doneCounter += 1;
results.push(res);
if (doneCounter === arr.length) {
cb(results);
}
});
});
}
eachAsync(items, someAsyncFunc, console.log);
Теперь запуск node iterasync.js
будет ждать около трех секунд, а затем распечатать [ 'ONE', 'TWO', 'THREE' ]
. Это простой пример, но он может быть расширен для обработки многих ситуаций.
Ответ 3
Как правильно указано, вы должны использовать setTimeout, например:
each_async = function(ary, fn) {
var i = 0;
-function() {
fn(ary[i]);
if (++i < ary.length)
setTimeout(arguments.callee, 0)
}()
}
each_async([1,2,3,4], function(p) { console.log(p) })
Ответ 4
Взгляните на этот проект: noodles. Он предоставляет асинхронные интерфейсы для всех методов экстрасети массивов.
Ответ 5
Существует новый стандарт асинхронной итерации: https://github.com/tc39/proposal-async-iteration
Это добавляет генераторы async и async для циклов. Новый цикл for await
поддерживает преобразование из синхронных итераций, и ожидание блокирует цикл.
async function() {
for await(let value of [ 1, 2 ]) {
await(Promise.resolve())
console.log(value)
}
}
Он реализуется в Chrome/ Node.js и Firefox.