Javascript Node.js:执行多个异步操作的最佳方法,然后再执行其他操作?
在下面的代码中,我试图一次完成多个(大约10个)HTTP请求和RSS解析 我在需要访问和解析结果的URI数组上使用标准的Javascript Node.js:执行多个异步操作的最佳方法,然后再执行其他操作?,javascript,node.js,promise,Javascript,Node.js,Promise,在下面的代码中,我试图一次完成多个(大约10个)HTTP请求和RSS解析 我在需要访问和解析结果的URI数组上使用标准的forEach构造 代码: 我知道当调用函数一次时,我应该使用回调。然而,在本例中,我能想到的使用回调的唯一方法是调用一个函数,该函数计算它被调用的次数,并且只有当它被调用的次数与feedsToFetch.length相同时才会继续,这似乎有点骇人听闻 所以我的问题是,在node.js中处理这种情况的最佳方法是什么 最好没有任何形式的阻塞!(我仍然想要那炽热的速度)。是承诺还是
forEach
构造
代码:
我知道当调用函数一次时,我应该使用回调。然而,在本例中,我能想到的使用回调的唯一方法是调用一个函数,该函数计算它被调用的次数,并且只有当它被调用的次数与feedsToFetch.length
相同时才会继续,这似乎有点骇人听闻
所以我的问题是,在node.js中处理这种情况的最佳方法是什么
最好没有任何形式的阻塞!(我仍然想要那炽热的速度)。是承诺还是别的什么
谢谢,
Danny使用url列表的副本作为队列来跟踪到达的情况,这样做很简单: (所有更改均已评论)
最后,这并没有承诺的那么“肮脏”,并且为您提供了重新加载未完成URL的机会(仅计数器无法告诉您哪个失败)。对于这个简单的并行下载任务,它实际上将向您的项目中添加更多的代码实现承诺,而不是简单的队列,而且Promise.all()不是最直观的地方。一旦您进入子查询,或者想要比火车失事更好的错误处理,我强烈建议您使用承诺,但您不需要使用火箭发射器来杀死松鼠…无需任何黑客操作 我建议使用该模块,因为它使这类事情变得更容易
async
作为arr.forEach
的异步替代品提供,并允许您在完成后传递done
回调函数。它将处理一系列的每个项目,就像forEach
一样。此外,它还可以方便地将错误冒泡到回调中,这样就不必在循环中包含处理程序逻辑。如果需要并行处理,可以使用
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!");
});
或者,您可以构建一个异步操作数组并将其传递给。Series将以系列处理结果(非并行),并在每个函数完成时调用回调。如果您更喜欢熟悉的arr.forEach
语法,则可以在async.eachSeries
上使用此选项
// 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,以便于理解其工作原理
一旦我们删除了这里的注释,我们只有9行代码可以被异步处理的任何数组重用!它不会修改原始数组,可以发送错误以“短路”迭代,并且可以使用单独的回调。它还可以处理空数组。只需9行即可实现相当多的功能:)
现在,让我们创建一个示例异步函数来使用它。我们将在这里用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);
}
输出(每次输出前延迟500毫秒)
无黑客解决方案 流行的Promise库为此用例提供了一个
.all()
方法(等待一系列异步调用完成,然后执行其他操作)。它与你的场景完美匹配
Bluebird还有.map()
,它可以获取一个值数组并使用它来启动承诺链
下面是一个使用蓝鸟.map()
的示例:
还要注意,您不需要在这里使用闭包来累积结果
这些代码也写得很好,有很多例子,因此更容易理解
一旦我学会了承诺模式,生活就变得轻松多了。我怎么推荐都不够
此外,这里还介绍了使用Promission、async
模块和其他模块处理异步函数的不同方法
希望这有帮助 是的,承诺是最简单的方式。如果您想使用标准JS编写自己的解决方案,可以很容易地进行调整。请显示
feed()
函数的代码。@Amadan,也许这是个人偏好,但我认为承诺不是“最简单”的方法。我已经提供了一个答案来进一步说明这一点。您已经找到了答案:使用计数器并计算提出和完成的请求数。这基本上就是async和Promissions在内部所做的。大约在异步库编写前一年,我写了这个答案来解决这个确切的问题:。这是一个更高级的实现,允许您启动批量异步操作:@naomik:我想说的是个人偏好-我看不出aarosil的答案比您的答案复杂得多。注意,是node/v8 vNext,它会有收益。这里的asyncEach的行为更像async.eachSeries
而不是async.each
fwiw<代码>异步。每个都将以最快的速度(即并发)发出http请求(或其他任何请求)asynSeries
(或此处的“串行”解决方案)将一个接一个地执行。“除非我弄错了。”红杉道,接得好。我已将自定义的asyncEach
重命名为asyncForEach
,以表示它执行类似arr.forEach
的串行处理。我还将async.each
更新为async.eachSeries
。谢谢“不需要黑客……使用这个库”-我不遵循这个逻辑,另一方面,NodeJS附带的承诺会让这个变得微不足道。@BenjaminGruenbaum所以当你说node时
// 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 !");
});
// 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();
}
// 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");
});
async cat !
async hat !
async wat !
done
var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));
function processAllFeeds(feedsToFetch) {
return Promise.map(feedsToFetch, function(feed){
// I renamed your 'feed' fn to 'processFeed'
return processFeed(feed)
})
.then(function(articles){
// 'articles' is now an array w/ results of all 'processFeed' calls
// do something with all the results...
})
.catch(function(e){
// feed server was down, etc
})
}
function processFeed(feed) {
// use the promisified version of 'get'
return request.getAsync(feed.url)...
}