Взломы не требуются
Я бы порекомендовал использовать модуль async, так как он значительно упрощает такие вещи.
async
предоставляет async.eachSeries в качестве асинхронной замены arr.forEach
и позволяет передать done
функция обратного вызова, когда она завершена. Он будет обрабатывать каждый элемент в серии, как это делает forEach
. Кроме того, он будет удобно отображать ошибки в вашем обратном вызове, чтобы вам не приходилось иметь логику обработчика внутри цикла. Если вам нужна/требуется параллельная обработка, вы можете использовать async.each. .
Между вызовом async.eachSeries
и обратным вызовом не будет блокировки.
async.eachSeries(feedsToFetch, function(feedUri, done) {
// call your async function
feed(feedUri, function(err, feedArticles) {
// if there's an error, "bubble" it to the callback
if (err) return done(err);
// your operation here;
articles = articles.concat(feedArticles);
// this task is done
done();
});
}, function(err) {
// errors generated in the loop above will be accessible here
if (err) throw err;
// we're all done!
console.log("all done!");
});
В качестве альтернативы вы можете создать массив асинхронных операций и передать их async.series. Series обработает ваши результаты последовательно (не параллельно) и вызовет обратный вызов после завершения каждой функции. Единственная причина использовать это вместо async.eachSeries
— это если вы предпочитаете знакомый синтаксис arr.forEach
.
// create an array of async tasks
var tasks = [];
feedsToFetch.forEach(function (feedUri) {
// add each task to the task array
tasks.push(function() {
// your operations
feed(feedUri, function(err, feedArticles) {
if (err) throw err;
articles = articles.concat(feedArticles);
});
});
});
// call async.series with the task array and callback
async.series(tasks, function() {
console.log("done !");
});
Или вы можете Roll Your Own™
Возможно, вы слишком амбициозны или не хотите полагаться на зависимость async
. Может быть, вам просто скучно, как и мне. В любом случае, я намеренно скопировал API async.eachSeries
, чтобы было проще понять, как это работает.
Как только мы удалим здесь комментарии, у нас останется всего 9 строк кода, которые можно повторно использовать для любого массива, который мы хотим обрабатывать асинхронно! Он не будет изменять исходный массив, ошибки могут быть отправлены для «короткого замыкания» итерации, и можно использовать отдельный обратный вызов. Он также будет работать с пустыми массивами. Совсем немного функционала всего за 9 строк :)
// void asyncForEach(Array arr, Function iterator, Function callback)
// * iterator(item, done) - done can be called with an err to shortcut to callback
// * callback(done) - done recieves error if an iterator sent one
function asyncForEach(arr, iterator, callback) {
// create a cloned queue of arr
var queue = arr.slice(0);
// create a recursive iterator
function next(err) {
// if there's an error, bubble to callback
if (err) return callback(err);
// if the queue is empty, call the callback with no error
if (queue.length === 0) return callback(null);
// call the callback with our task
// we pass `next` here so the task can let us know when to move on to the next task
iterator(queue.shift(), next);
}
// start the loop;
next();
}
Теперь давайте создадим образец асинхронной функции для использования с ним. Здесь мы имитируем задержку с setTimeout
500 мс.
// void sampleAsync(String uri, Function done)
// * done receives message string after 500 ms
function sampleAsync(uri, done) {
// fake delay of 500 ms
setTimeout(function() {
// our operation
// <= "foo"
// => "async foo !"
var message = ["async", uri, "!"].join(" ");
// call done with our result
done(message);
}, 500);
}
Хорошо, давайте посмотрим, как они работают!
tasks = ["cat", "hat", "wat"];
asyncForEach(tasks, function(uri, done) {
sampleAsync(uri, function(message) {
console.log(message);
done();
});
}, function() {
console.log("done");
});
Выход (задержка 500 мс перед каждым выходом)
async cat !
async hat !
async wat !
done
person
Mulan
schedule
09.10.2014
feed()
. - person jfriend00   schedule 09.10.2014serial
от @naomik ниже - единственный, который сохранит порядок, если я не ошибаюсь. - person Stop Slandering Monica Cellio   schedule 09.10.2014