按顺序运行大量承诺

时间:2014-12-05 15:59:19

标签: node.js promise

一个nodejs项目。我曾尝试使用promises执行大量(约100k)任务。我能做的就是将它转换为带有Q的workOnebyOne函数。有更好的方法吗?

function workOnebyOne(items, worker) {
  var deferred = Q.defer()

  function _doNext() {
    if (items.length === 0) {
      deferred.resolve()
      return
    }
    var item = items[0]
    synchronize(worker, item)
      .then(function (result) {
        items = items.slice(1)
        deferred.notify({
          item: item,
          result: result
        })
        _doNext()
      }, function () {
        items = items.slice(1)
        _doNext()
      })
  }

  _doNext()

  return deferred.promise
}

utils.workOnebyOne(tasks, workerFunction)

3 个答案:

答案 0 :(得分:2)

您基本上是在这里重新实施排队。在Bluebird的承诺中(你也可以使用Promise.each,速度更快,内存消耗更少,有助于100K任务)。

在Q中你通常可以在tasks数组上使用.reduce来立即对它们进行排队 - 但是在Q promises中创建100K承诺队列的100K元素会崩溃节点(再次,这是Q,Bluebird或承诺时) )会处理它就好了)。这个(这里不正确)解决方案看起来像:

var res = tasks.reduce(function(p, c){
    return p.then(function(){ return workerFunction(c); });
}, Q());

对于短队列(Q中的< 500 promises),这很有效。

因此,由于旧的库选择,并且由于涉及大量的承诺,您无法优雅地实际解决它,使用类似方法的回调队列非常接近您的唯一方式。我也避免notify因为它被移除(即使是Q),并且通常是一个糟糕的API(不能很好地构成)。

答案 1 :(得分:1)

我花了一些时间寻找简单而优雅的解决方案。我只找到了一些提示和讨论但没有准备好的例子。最后,我发现了对https://github.com/kriskowal/q/issues/606的讨论,因此对我有用的东西可以像这样被孤立和概括:

function workOneByOne(items, someAsyncFuntionReturningPromise) {
  var lastResultPromise = items
    .map(function(item) {
      return function(previousResult) {
        /*
         * this function has to:
         * - process result from previous item processed if any
         * - call the async job
         * - return promise of the job done
         */
        if (previousResult) {
          // process result here
        }

        return someAsyncFuntionReturningPromise(item);
    }})
    .reduce(Q.when, Q());

    return lastResultPromise;
}

如果没有可用的函数返回承诺,您可以使用

调用上面的内容
workOneByOne(items, Q.nfbind(someAsyncFunctionWithCallback))

答案 2 :(得分:1)

如果await / async可用,则为超简单解决方案。

// functions is an array of functions that return a promise.
async function runInSequence(functions) {
  const results = [];

  for (const fn of functions) {
    results.push(await fn());
  }

  return results;
}

我们可以像这样使用它:

/**
 * Waits "delay" miliseconds before resolving the promise.
 * @param {Number} delay The time it takes to resolve the promise.
 * @param {any} value The resolving value.
 */
function promisedFunction(delay, value) {
  return new Promise(resolve => {
    setTimeout(() => resolve(value), delay);
  });
}

console.time("execution");
runInSequence([
    promisedFunction.bind(this, 1000, 1),
    promisedFunction.bind(this, 1000, 2),
    promisedFunction.bind(this, 1000, 3)
]).then(results => {
    console.timeEnd("execution");
    console.log(results);
});

有效的RunKit示例here