Q promises中的并发限制 - 节点

时间:2014-03-12 23:59:14

标签: javascript node.js asynchronous concurrency q

有没有任何方法可以使用Q promises库限制promises的并发性?

这个问题与How can I limit Q promise concurrency?

有点相关

但问题是我试图做这样的事情:

for (var i = 0; i <= 1000; i++) {
  return Q.all([ task1(i), task2(i) ]); // <-- limit this to 2 at a time.
}

真实用例是:

  1. 从数据库中获取帖子
  2. 循环数据库中的每个帖子,例如posts.forEach(function(post) {}
  3. 为每个帖子执行task1,task2,task3(检索社交计数器,检索评论计数等)
  4. 在数据库中保存新的帖子数据。
  5. 但问题是节点正在同时执行所有帖子的所有任务,比如要求facebook&#34; like count&#34;同时为500个帖子。

    我如何限制Q.all()所以一次只有2个帖子正在执行他们的任务?或者其他可能的解决方案可以应用于何处?

    注意:大多数任务(如果不是全部)都依赖于request library

4 个答案:

答案 0 :(得分:3)

感谢Dan,他的answer以及他将其与我的代码集成的帮助,可以使用他的gist和这样的snipplet来完成:

var qlimit = require('../libs/qlimit');

var test = function(id) {
  console.log('Running ' + id);
  return Q.nfcall(request, 'some dummy url which takes some time to process, for example a php file with sleep(5)').spread(function(response, body) {
    console.log('Response ' + id);
    return body;
  });
}

test = qlimit.limitConcurrency(test, 1);

var data = [0, 1, 2];

data.forEach(function(id) {
  console.log('Starting item ' + id);
  Q.all([ test(id) ]);
});

这样你会得到类似的东西:

  • 开始第0项
  • 开始第1项
  • 开始第2项
  • 正在运行0
  • 回复0
  • 运行1
  • 回复1
  • 跑步2
  • 回复2

一次显然是1个请求。

我在实现中缺少的重点是你需要在启动循环之前使用limitConcurrency重新声明函数,而不是在它内部。

答案 1 :(得分:1)

几天前我问过一个非常类似的问题:Node.js/Express and parallel queues

我找到的解决方案(参见我自己的回答)是使用Caolan's async。它允许您创建“操作队列”,并且您可以限制可以并发运行的数量:请参阅“队列”方法。

在您的情况下,Node的主循环将从Q中提取元素,并在队列中为每个元素创建一个任务。你也可以限制它(所以不要基本上重新创建Q之外的队列),例如,只有在最后一个元素被执行时才向队列添加N个新元素(“队列”方法的“空”回调) )。

答案 2 :(得分:1)

Here是我用来限制q承诺的代码。

我刚把它从我需要它的项目中删除了。如果有更多人感兴趣,我可以将其分成一个模块或其他东西。

答案 3 :(得分:0)

查看方法spex.pagespex.sequence。它们旨在实现data throttling + load balancing承诺的任何可能策略。

请参阅以下项目文档中的几个例子。

平衡页面来源

以下示例使用方法page启动5个页面的序列,然后将已解析的数据记录到控制台中。源函数为每个页面提供半秒延迟。

var $q = require('q');    
var spex = require('spex')($q);

function source(index, data, delay) {
    return new $q.Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve([
                "page-" + index, // simple value;
                $q.resolve(Date.now()) // promise value;
            ])
        }, 500); // wait 1/2 second before serving the next page;
    });
}

function logger(index, data, delay) {
    console.log("LOG:", data);
}

spex.page(source, {dest: logger, limit: 5})
    .then(function (data) {
        console.log("FINISHED:", data);
    });

输出:

LOG: [ 'page-0', 1446050705823 ]
LOG: [ 'page-1', 1446050706327 ]
LOG: [ 'page-2', 1446050706834 ]
LOG: [ 'page-3', 1446050707334 ]
LOG: [ 'page-4', 1446050707839 ]
FINISHED: { pages: 5, total: 10, duration: 2520 }

平衡序列接收器

在下面的示例中,我们有一个sequence,它在索引小于5时返回数据,并且目标函数在处理从源解析的每个数据时强制执行1秒延迟。

var $q = require('q');    
var spex = require('spex')($q);

function source(index, data, delay) {
    console.log("SOURCE:", index, data, delay);
    if (index < 5) {
        return $q.resolve(index);
    }
}

function dest(index, data, delay) {
    console.log("DEST:", index, data, delay);
    return new $q.Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, 1000);
    });
}

spex.sequence(source, dest)
    .then(function (data) {
        console.log("DATA:", data);
    });

输出:

SOURCE: 0 undefined undefined
DEST: 0 0 undefined
SOURCE: 1 0 1011
DEST: 1 1 1001
SOURCE: 2 1 1001
DEST: 2 2 1001
SOURCE: 3 2 1000
DEST: 3 3 1000
SOURCE: 4 3 1001
DEST: 4 4 1001
SOURCE: 5 4 1000
DATA: { total: 5, duration: 5013 }