nodejs究竟如何承诺库为多个承诺工作?

时间:2014-10-19 20:06:22

标签: javascript node.js parallel-processing web-scraping promise

最近我使用'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#

中更快地解决这个问题

3 个答案:

答案 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);

此优点

  • 没有外部依赖关系或beta apis
  • 可以在任何要在
  • 上执行异步任务的阵列上重复使用
  • 非阻塞,正如您对节点
  • 所期望的那样
  • 可以很容易地适应并行处理
  • 通过编写自己的实用程序,您可以更好地理解这种事情是如何工作的

如果您只想抓取一个模块来帮助您,请查看asyncasync.eachSeries方法。

答案 1 :(得分:2)

你传递的内容Promise.all()是一系列承诺。它完全知道没有关于这些承诺的背后是什么。它所知道的是,这些承诺将在未来的某个时间得到解决或拒绝,并且它将创建一个新的主承诺,它遵循您传递的所有承诺的总和。这是承诺的好处之一。它们是一种抽象,可以让您协调任何类型的操作(通常是异步的),而不考虑它是什么类型的操作。因此,承诺与实际行动毫无关系。他们所做的就是监控行动的完成或错误,然后根据承诺向那些代理报告。其他代码实际上运行该操作。

在您的特定情况下,您会立即在紧急循环中调用http.get(),而您的代码(与承诺无关)会立即启动大量http.get()操作。这些将被激活,因为底层传输可以做到这一点(可能受连接限制)。

如果您希望它们可以串行启动或者一次启动10个批次,那么您必须自己编写代码。承诺与此无关。

您可以使用promises来帮助您对它们进行串行或批量启动进行编码,但是这需要额外的代码才能实现这一目标。

Async库是专门为并行运行而构建的,但在任何给定时间都有最大数量的飞行,因为这是一个常见的方案,你要么有你的连接限制,要么你没有#39} ; t想要压倒接收服务器。您可能对parallelLimit选项感兴趣,该选项允许您并行运行多个异步操作,但在任何给定时间都有最大数量的飞行操作。

答案 2 :(得分:0)

首先,澄清: promise 确实代表计算的未来结果,没有别的。它不代表任务或计算本身,这意味着它不能被称为"或者"解雇"。

您的脚本会立即创建所有数千个承诺,并且每个创建都会立即调用http.get。我怀疑http库(或它所依赖的东西)有一个连接池,它具有并行请求的数量限制,并且隐含地推迟了其余的请求。

Promise.all没有做任何"处理" - 它不负责启动任务和解决通过的承诺。它只会监听它们并检查它们是否都已准备好,并返回对最终结果的承诺。