Re: [Q] limiting concurrency?

170 views
Skip to first unread message

Christoph Dorn

unread,
Oct 25, 2012, 11:40:00 AM10/25/12
to q-con...@googlegroups.com
24 October, 2012 6:37 PM
What is the best pattern for limiting the concurrency of resolving an array (queue) of promises?  I'd like to have another argument to Q.all (or qq's queue?) that would allow at most a specified number of the functions being processed at once.

Here is one solution: https://github.com/sourcemint/util-js/blob/master/lib/q-throttle.js

Christoph

Forbes Lindesay

unread,
Oct 26, 2012, 6:40:17 AM10/26/12
to q-con...@googlegroups.com
What you're asking for doesn't really make sense.  An array of promises (such as that you'd pass to Q.all) is an array of tasks that have already been started.  Waiting on them to resolve is, to all intents and purposes, free.  Waiting on them one at a time, versus in parallel has very little difference (one has slightly fewer turns of the event loop).  It's when you call the functions that you begin the tasks, so what you really need to do is throttle how many times you call the function simultaniously:

You could try something like this:

```
function throttle(functions, max) {
  var def = Q.defer();
  var result = [];
  var running = 0;
  var waitingAt;
  function next(i) {
    if (running > max) {
      waitingAt = i;
    } else if (i < functions.length) {
      var res = functions[i]();
      result.push(res);
      res.then(completeTask, completeTask);
    } else {
      def.resolve(Q.all(result));
    }
  }
  next(0);
  function completeTask() {
    if (waitingAt) {
      next(waitingAt);
      waitingAt = null;
    }
  }
  return def.promise;
}
```

I haven't tested it, but it should take an array of functions and a max-concurrency value, then execute up to max of them in parallel and return a promise for an array of the results.

Wout Mertens

unread,
Nov 23, 2012, 10:27:41 AM11/23/12
to q-con...@googlegroups.com
On Thursday, October 25, 2012 3:37:25 AM UTC+2, Jim Tittsler wrote:
What is the best pattern for limiting the concurrency of resolving an array (queue) of promises?  I'd like to have another argument to Q.all (or qq's queue?) that would allow at most a specified number of the functions being processed at once.

I solved it by using async and creating a queue that promises to give you a callback to call when you're done.

Coffeescript:
Q = require 'q'
async = require 'async'

promisQue = async.queue(
  (deferred, callback) ->
    deferred.resolve(callback)
  3
)

throttledPromise = () ->
  deferred = Q.defer()
  promisQue.push deferred
  deferred.promise

for i in [1..50]
  ((i) ->
    throttledPromise().then (cb) ->
      setTimeout cb, 500
      console.log i
  )(i) 

Javascript:
var Q, async, i, promisQue, throttledPromise, _fn, _i;

Q = require('q');

async = require('async');

promisQue = async.queue(function(deferred, callback) {
  return deferred.resolve(callback);
}, 3);

throttledPromise = function() {
  var deferred;
  deferred = Q.defer();
  promisQue.push(deferred);
  return deferred.promise;
};

_fn = function(i) {
  return throttledPromise().then(function(cb) {
    setTimeout(cb, 500);
    return console.log(i);
  });
};
for (i = _i = 1; _i <= 50; i = ++_i) {
  _fn(i);
}

Thoughts? (the anon (i) function is needed to keep a copy of i for the promise)

Wout.

Wout Mertens

unread,
Nov 23, 2012, 11:36:06 AM11/23/12
to q-con...@googlegroups.com
On Friday, November 23, 2012 4:27:41 PM UTC+1, Wout Mertens wrote:
On Thursday, October 25, 2012 3:37:25 AM UTC+2, Jim Tittsler wrote:
What is the best pattern for limiting the concurrency of resolving an array (queue) of promises?  I'd like to have another argument to Q.all (or qq's queue?) that would allow at most a specified number of the functions being processed at once.

I solved it by using async and creating a queue that promises to give you a callback to call when you're done.

Now using a class and showing how to inject another promise:

Q = require 'q'
async = require 'async'

class promisQue
  resolver = (deferred, callback) -> deferred.resolve(callback)

  constructor: (num) ->
    @queue = async.queue resolver, num or 3

  P: () ->
    deferred = Q.defer()
    @queue.push deferred
    deferred.promise

q = new promisQue

lastP = null

for i in [1..20]
  ((i) ->
    lastP = q.P().then (cb) ->
      Q.delay(i, 500).then(-> console.log i).finally cb
  )(i)

lastP.then -> console.log "Done!"


Reply all
Reply to author
Forward
0 new messages