最近我使用'promise'在nodejs中创建了一个webscrapper。我为每个想要抓取的网址创建了一个Promise,然后使用了所有方法:
var fetchUrlArray=[];
for(...){
var mPromise = new Promise(function(resolve,reject){
(http.get(...))()
});
fetchUrlArray.push(mPromise);
}
Promise.all(fetchUrlArray).then(...)
有成千上万的网址,但只有少数网站已经超时。我得到的印象是它一次并行处理5个承诺。 我的问题是promise.all()究竟是如何工作的。可以: 逐个调用每个承诺并切换到下一个承诺,直到前一个承诺得到解决。 或者处理来自阵列的一批中的承诺。 或者它是否会触发所有承诺
在nodejs中解决此问题的最佳方法是什么。因为现在我可以在Java / C#
中更快地解决这个问题答案 0 :(得分:2)
我会这样做
就个人而言,我并不是Promises的忠实粉丝。我认为API非常冗长,结果代码很难阅读。下面定义的方法会产生非常平坦的代码,并且更容易立即了解正在进行的操作。至少是imo。
这是我为this question
的答案创建的一个小东西// 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();
}
您可以像这样使用
var urls = [
"http://example.com/cat",
"http://example.com/hat",
"http://example.com/wat"
];
function eachUrl(url, done){
http.get(url, function(res) {
// do something with res
done();
}).on("error", function(err) {
done(err);
});
}
function urlsDone(err) {
if (err) throw err;
console.log("done getting all urls");
}
asyncForEach(urls, eachUrl, urlsDone);
此优点
如果您只想抓取一个模块来帮助您,请查看async和async.eachSeries方法。
答案 1 :(得分:2)
你传递的内容Promise.all()
是一系列承诺。它完全知道没有关于这些承诺的背后是什么。它所知道的是,这些承诺将在未来的某个时间得到解决或拒绝,并且它将创建一个新的主承诺,它遵循您传递的所有承诺的总和。这是承诺的好处之一。它们是一种抽象,可以让您协调任何类型的操作(通常是异步的),而不考虑它是什么类型的操作。因此,承诺与实际行动毫无关系。他们所做的就是监控行动的完成或错误,然后根据承诺向那些代理报告。其他代码实际上运行该操作。
在您的特定情况下,您会立即在紧急循环中调用http.get()
,而您的代码(与承诺无关)会立即启动大量http.get()
操作。这些将被激活,因为底层传输可以做到这一点(可能受连接限制)。
如果您希望它们可以串行启动或者一次启动10个批次,那么您必须自己编写代码。承诺与此无关。
您可以使用promises来帮助您对它们进行串行或批量启动进行编码,但是这需要额外的代码才能实现这一目标。
Async库是专门为并行运行而构建的,但在任何给定时间都有最大数量的飞行,因为这是一个常见的方案,你要么有你的连接限制,要么你没有#39} ; t想要压倒接收服务器。您可能对parallelLimit选项感兴趣,该选项允许您并行运行多个异步操作,但在任何给定时间都有最大数量的飞行操作。
答案 2 :(得分:0)
首先,澄清: promise 确实代表计算的未来结果,没有别的。它不代表任务或计算本身,这意味着它不能被称为"或者"解雇"。
您的脚本会立即创建所有数千个承诺,并且每个创建都会立即调用http.get
。我怀疑http
库(或它所依赖的东西)有一个连接池,它具有并行请求的数量限制,并且隐含地推迟了其余的请求。
Promise.all
没有做任何"处理" - 它不负责启动任务和解决通过的承诺。它只会监听它们并检查它们是否都已准备好,并返回对最终结果的承诺。