我如何将承诺组合在一起?

时间:2014-04-09 23:19:04

标签: javascript promise q

我正在使用Q javascript promises库并且正在浏览器中运行,我想弄清楚如何将Promises组链接在一起,以便每个组按顺序执行。例如,如果我有项目A,B,C和D,我想将A和B组合在一起,然后将C和D组合在一起,这样A和B都必须在C和D执行之前完成。我创建了这个simple jsfiddle以显示我当前的尝试。

var work_items = [ 'A','B','C','D','E','F','G','H','I' ];
var n = 2;    // group size
var wait = 1000;

var getWorkPromiseFn = function (item) {
    log("Getting promise function for " + item);
    return function () {
        log("Starting " + item);
        var deferred = Q.defer();
        setTimeout(function () {
            var status = "Finished " + item;
            log(status);
            deferred.resolve(status);             
        }, wait);
        return deferred.promise;
    };
};

var queue = Q();

//log('Getting sequentially');    // One-by-one in sequence works fine
//work_items.forEach(function (item) {
//    queue = queue.then(getWorkPromiseFn(item));
//});

log('Getting ' + n + ' at a time'); // This section does not        
while (work_items.length > 0) {
    var unit = [];
    for (var i=0; i<n; i++) {
        var item = work_items.shift();
        if (item) {
            unit.push(getWorkPromiseFn(item));               
        }
    }
    queue.then(Q.all(unit));
}
var inspect = queue.inspect(); // already fulfilled, though no work is done

看起来我可能在这里将错误的数组传递给Q.all,因为我传递了一个函数数组,这些函数返回promises而不是promise数组本身。当我尝试直接使用promises时(例如unit.push(Q().then(getWorkPromiseFn(item));),每个人的工作立即开始,没有顺序处理。我想我基本上不清楚以适当推迟小组执行的方式代表小组的好方法。

那么,我怎样才能推迟执行这样的一组承诺?

4 个答案:

答案 0 :(得分:1)

这可以通过首先将项目数组预处理成组,然后在标题&#34; The Collection Kerfuffle&#34;标题下应用here提供的两种模式(不是反模式)来完成。

主程序可以编码为单链数组方法。

var work_items = [ 'A','B','C','D','E','F','G','H','I' ];
var wait = 3000;

//Async worker function
function getWorkPromise(item) {
    console.log("Starting " + item);
    var deferred = Q.defer();
    setTimeout(function () {
        var status = "Finished " + item;
        console.log(status);
        deferred.resolve(status);             
    }, wait);
    return deferred.promise;
};

function doAsyncStuffInGroups(arr, n) {
    /* 
     * Process original array into groups, then 
     * process the groups in series, 
     * progressing to the next group 
     * only after performing something asynchronous 
     * on all group members in parallel.
     */
    return arr.map(function(currentValue, i) {
        return (i % n === 0) ? arr.slice(i, i+n) : null;
    }).filter(function(item) {
        return item;
    }).reduce(function(promise, group) {
        return promise.then(function() {
            return Q.all(group.map(function(item) {
                return getWorkPromise(item);
            }));
        });
    }, Q());
}

doAsyncStuffInGroups(work_items, 2).then(function() {
    console.log("All done");
});

请参阅 fiddle 。延迟3秒让您有时间欣赏正在发生的事情。我发现1s太快了。

这样的解决方案优雅而简洁,但非常难以理解。在生产代码中,我会提供更多评论,以帮助任何追随我的人。

记录:

  • 开放arr.map(...).filter(...)处理arr(非破坏性地)进入数组数组,每个内部数组代表一组长度为n(加上终端余数)。
  • 链式.reduce(...)是一个异步&#34;序列化器&#34;图案。
  • 嵌套Q.all(group.map(...))是一个异步&#34;并行化程序&#34;图案。

答案 1 :(得分:0)

承诺的.then函数不会改变承诺,所以当你这样做时:

 p.then(function(){
         // stuff
 });

您根本不会更改承诺p,而是需要将其分配给某些内容:

p = p.then(....)

这就是为什么您的queue承诺始终得到解决,它永远不会超过Q()

在您的情况下,更改:

queue.then(Q.all(unit));

分为:

queue = queue.then(function(){ return Q.all(unit); });

或者在ES6承诺和库中使用像Bluebird这样的语法,提到了另一个答案:

queue = queue.then(function(){ return Promise.all(unit); });

答案 2 :(得分:0)

最让我困惑的是,链接的异步函数需要返回一个返回promise的函数。这是一个例子:

function setTimeoutPromise(ms) {
  return new Promise(function (resolve) {
    setTimeout(resolve, ms);
  });
}

function foo(item, ms) {
  return function() {
    return setTimeoutPromise(ms).then(function () {
      console.log(item);
    });
  };
}

var items = ['one', 'two', 'three'];

function bar() {
  var chain = Promise.resolve();
  for (var i in items) {
    chain = chain.then(foo(items[i], (items.length - i)*1000));
  }
  return chain.then();
}

bar().then(function () {
  console.log('done');
});

请注意,foo返回一个返回promise的函数。 foo()直接返回承诺。

请参阅此Live Demo

答案 3 :(得分:-1)

我建议你使用bluebird,它是那里最好的性能承诺,https://github.com/petkaantonov/bluebird

链的示例也应该在https://github.com/petkaantonov/bluebird#how-do-long-stack-traces-differ-from-eg-q