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

Reducing all JS Promises before resolving to next then

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

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

非常感谢您的建议。请在投票前发表评论。

您可以使用 promise.all() 实现此目的。只需将您的每个数组承诺推入一个数组(通常名称为 deferred)然后 return promise.all(deferred)

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

更新

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

希望它能澄清一些有关 promise 对象的信息,因为它们可能会造成混淆。

我认为您会在 start 之后立即获得 done,因为您当时没有返回 workCollection(results) 的结果,因此它会立即通过 undefined 得到解决。试试这个

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

此外,如建议的那样,如果顺序无关紧要,请使用 Promise.all 否则您的 Promise Waterfall 方法也如 here 所述是好的。

有很多事情:

  • 如前所述,您需要 return 来自 then 回调的 `` 的(承诺)结果,否则链不会等待它
  • q().then(function(){return true}) 应该只是 q(true)
  • IIFE 没用。如果您需要任何闭包作用域,reduce 回调已经提供了它。
  • 您不能 return true 并期望任何事情都在等待 setTimeout。您需要 promisify it (wrap it in a new Q.Promise(…)), or just use 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);
});

你犯的错误是(除了没有返回 workCollection promise)setTimeout 没有被 promisified,你可以像这样修复它:

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 但其余代码是相同的