在解决到下一个之前减少所有JS Promises

时间:2016-04-20 22:43:57

标签: javascript promise q

我似乎无法找到解决此问题的正确方法。我有一大堆的承诺,并且在其中一个解析器中,我想暂停并在数组上做一系列的工作(使用promises),我不希望这个实际解决,直到一系列的承诺有完成。这个例子似乎让我非常接近,但我想我可能已经创建了一个反模式。

http://taoofcode.net/promise-anti-patterns/

// async_series.js
// example of using reduce and promises and closure to create async results in series

var q = require('Q');
var results = [1, 2, 3, 4, 5];

function workCollection(arr) {

    return arr.reduce(function(promise, item, index) {

        return promise.then(function(result) {

            return (function(i, r, idx){
                setTimeout(function(){
                    console.log('item', i, 'result', r, 'index', idx);
                }, 1000 * idx);
                return true
            })(item, result, index);

        });

    }, q().then(function(){return true}));

}

    q()
        .then(function(){
            console.log('start');
        })
        .then(function(){
            workCollection(results)
        })
        .then(function(){
            console.log('done');
        })
        .catch(function(err){
            console.log(err);
        });

有点工作,但输出是:

start
done
item 1 result true index 0
item 2 result true index 1
item 3 result true index 2
item 4 result true index 3
item 5 result true index 4

与此相反(预期):

start
item 1 result true index 0
item 2 result true index 1
item 3 result true index 2
item 4 result true index 3
item 5 result true index 4
done

建议非常感谢。 Plz在投票前发表评论。

4 个答案:

答案 0 :(得分:1)

您可以使用promise.all()实现此目的。只需将每个数组promises推送到一个数组中(通常名为deferred),然后返回promise.all(deferred)

此处的文档:https://github.com/kriskowal/q/wiki/API-Reference#promise-for-array-methods

<强>更新

我创建了一个使用deferred的词,而不是编辑你的小提琴,这是你在这种情况下应该使用的。 https://jsfiddle.net/twler/aujqcmhv/1/

希望它能清除一些关于promise对象的内容,因为它们可能令人困惑。

答案 1 :(得分:1)

我认为您在done之后立即收到start,因为您未在当时返回workCollection(results)的结果,因此立即通过undefined解决问题。试试这个

.then(function(){
    return workCollection(results)
})

另外,正如建议的那样,如果订单无关紧要,请与Promise.all一起使用,否则Promise Waterfall方法也会如here所述。

答案 2 :(得分:1)

有很多事情:

  • 如前所述,你需要return来自then回调的“承诺”结果,否则链条不会等待它
  • q().then(function(){return true})应该只是q(true)
  • IIFE没用。如果您需要任何关闭范围,reduce回调已经提供了它。
  • 你不能return true并期待等待setTimeout的任何事情。您需要promisify它(将其打包在new Q.Promise(…)中),或者只使用Q.delay

总而言之,您的代码应该如下所示:

var q = require('Q');

function workCollection(arr) {
    return arr.reduce(function(promise, item, index) {
        return promise.then(function(result) {
            return Q.delay(result, 1000);
        }).then(function(result) {
            console.log('item', item, 'result', result, 'index', index);
            return true;
        });
    }, q(true));
}

q().then(function(){
   console.log('start');
   return [1, 2, 3, 4, 5];
}).then(workCollection).then(function(result) {
   console.log('done', result);
}, function(err){
   console.error(err);
});

答案 3 :(得分:1)

你犯的错误是(除了不回复workCollection承诺)setTimeout没有被承诺,你可以修改它:

var q = require('Q');
var results = [1, 2, 3, 4, 5];

function delayedCall(i, r, idx){
  return q.Promise(function(resolve, reject){  
    setTimeout(function(){
      console.log('item', i, 'result', r, 'index', idx);
      resolve(true); 
    }, 1000 * idx);
  });
}

function workCollection(arr) {
  return arr.reduce(function(promise, item, index) {
    return promise.then(function(result) {
      return delayedCall(item, result, index);
    });
  }, q(true));
}

q().then(function(){
  console.log('start');
}).then(function(){
  return workCollection(results)
}).then(function(){
  console.log('done');
}).catch(function(err){
  console.log(err);
});

fiddle demo,我使用Q.defer代替Q.Promise,但其余代码相同