无需破解
我会推荐使用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 将在 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 !");
});
或者你可以自己动手™
也许您觉得自己更有野心,或者您不想依赖 async 依赖项。也许你只是像我一样无聊。无论如何,我特意复制了async.eachSeries 的API,以便于理解它是如何工作的。
一旦我们删除了这里的 cmets,我们就只有 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();
}
现在让我们创建一个示例异步函数来使用它。我们将在此处使用 500 毫秒的 setTimeout 来伪造延迟。
// 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