如何限制Q promise并发?

时间:2013-12-12 14:38:10

标签: javascript node.js concurrency promise q

如何编写限制Q promise并发的方法?

例如,我有一个方法spawnProcess。它返回Q承诺 我希望一次生成的进程不超过5个,但对调用代码透明。

我需要实现的是一个带签名的函数

function limitConcurrency(promiseFactory, limit)

我可以像

一样打电话
spawnProcess = limitConcurrency(spawnProcess, 5);

// use spawnProcess as usual

我已经开始研究我的版本,但我想知道是否有人有一个我可以检查的简洁实现。

4 个答案:

答案 0 :(得分:9)

我有一个库为您https://github.com/ForbesLindesay/throat

执行此操作

您可以通过browserify使用它或从brcdn(https://www.brcdn.org/?module=throat&version=latest)下载独立版本并将其添加为脚本标记。

然后(假设Promise构造函数是在您的环境中填充或实现的),您可以这样做:

//remove this line if using standalone build
var throat = require('throat');

function limitConcurrency(promiseFactory, limit) {
  var fn = throat(promiseFactory, limit);
  return function () {
    return Q(fn.apply(this, arguments));
  }
}

您可以直接致电throat(promiseFactory, limit),但这会返回promise承诺而不是Q承诺。

我也非常喜欢将它与array.map一起使用。

// only allow 3 parallel downloads
var downloadedItems = Q.all(items.map(throat(download, 3)));

答案 1 :(得分:2)

这似乎对我有用。

我不确定我是否可以简化它。 scheduleNextJob中的递归是必要的,因此running < limitlimit++始终以相同的刻度执行。

Also available as a gist.

'use strict';

var Q = require('q');

/**
 * Constructs a function that proxies to promiseFactory
 * limiting the count of promises that can run simultaneously.
 * @param promiseFactory function that returns promises.
 * @param limit how many promises are allowed to be running at the same time.
 * @returns function that returns a promise that eventually proxies to promiseFactory.
 */
function limitConcurrency(promiseFactory, limit) {
  var running = 0,
      semaphore;

  function scheduleNextJob() {
    if (running < limit) {
      running++;
      return Q();
    }

    if (!semaphore) {
      semaphore = Q.defer();
    }

    return semaphore.promise
      .finally(scheduleNextJob);
  }

  function processScheduledJobs() {
    running--;

    if (semaphore && running < limit) {
      semaphore.resolve();
      semaphore = null;
    }
  }

  return function () {
    var args = arguments;

    function runJob() {
      return promiseFactory.apply(this, args);
    }

    return scheduleNextJob()
      .then(runJob)
      .finally(processScheduledJobs);
  };
}

module.exports = {
  limitConcurrency: limitConcurrency
}

答案 2 :(得分:2)

Deferred promise实现具有gate功能,其功能完全相同:

spawnProcess = deferred.gate(spawnProcess, 5);    

答案 3 :(得分:2)

我写了一个小图书馆:https://github.com/suprememoocow/qlimit

它非常易于使用,专门设计用于Q承诺:

var qlimit = require('qlimit');
var limit = qlimit(2); // 2 being the maximum concurrency

// Using the same example as above
return Q.all(items.map(limit(function(item, index, collection) { 
  return performOperationOnItem(item);
}));

它还可以用于限制特定资源的并发性,如下所示:

var qlimit = require('qlimit');
var limit = qlimit(2); // 2 being the maximum concurrency

var fetchSomethingFromEasilyOverwhelmedBackendServer = limit(function(id) {
  // Emulating the backend service
  return Q.delay(1000)
    .thenResolve({ hello: 'world' }); 
});