如何将累积的返回Promise值作为数组返回到Array.prototype.reduce()之后的.then()?

时间:2015-11-13 08:18:46

标签: javascript arrays promise jquery-deferred

鉴于此模式

someArray.reduce(function(p, item) {
  return p.then(function() {
    return someFunction(item);
  });
}, $.Deferred().resolve()).then(function() {
  // all done here
  // access accumulated fulfilled , rejected `Promise` values
}, function err() {

});

在调用Promise后,有哪些方法可以将已完成的,被拒绝的.then(fulfilled)对象的累计值作为数组返回到.reduce()



function someFunction(index) {
  console.log("someFunction called, index = " + index);
  var $deferred = $.Deferred();

  window.setTimeout(function() {
    $deferred.resolve();
  }, 2000);

  return $deferred.promise();
}
   
var someArray = [1,2,3,4,5];

someArray.reduce(function(p, item) {
  return p.then(function() {
    return someFunction(item);
  });
}, $.Deferred().resolve()).then(function(data) {
  // all done here
  console.log(data, arguments) // `undefined` , `[]`
});

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:8)

根据您尝试做的具体情况,有多种可能的策略:以下是一个选项:

someArray.reduce(function(p, item) {
  return p.then(function(array) {
    return someFunction(item).then(function(val) {
        array.push(val);
        return array;
    });
  });
}, $.Deferred().resolve([])).then(function(array) {
  // all done here
  // accumulated results in array
}, function(err) {
  // err is the error from the rejected promise that stopped the chain of execution
});

工作演示:http://jsfiddle.net/jfriend00/d4q1aaa0/

仅供参考,Bluebird Promise库(我通常使用的)具有为此模式构建的.mapSeries()

var someArray = [1,2,3,4];

Promise.mapSeries(someArray, function(item) {
    return someFunction(item);
}).then(function(results) {
    log(results);
});

工作演示:http://jsfiddle.net/jfriend00/7fm3wv7j/

答案 1 :(得分:2)

可能的一种解决方案:

var $j = function(val, space) {
  return JSON.stringify(val, null, space || '')
}
var log = function(val) {
  document.body.insertAdjacentHTML('beforeend', '<div><pre>' + val + '</div></pre>')
}

var async = function(cur){
  var pro = new Promise(function(resolve, reject) {

        log('loading : ' + cur.name);
        
        // we simualate the loading
        setTimeout(function() {
          if(cur.name === 'file_3.js'){
            reject(cur.name);
          }
          resolve(cur.name);
        }, 1 * 1000);

      });

      return pro;
}

var files = '12345'.split('').map(function(v) {
  return {
    name: 'file_' + v + '.js', 
  }
});


var listed = files.reduce(function(t,v){

  t.p = t.p.then( function(){
    return async( v )
      .then(function(rep){
      
               t.fulfilled.push(rep);
               log('fulfilled :' + rep); 
               return rep;
      
      } , function(rep){
               
               t.rejected.push(rep);
               log('-----| rejected :' + rep); 
               return rep;
      }
   ).then(function(val){ t.treated.push(val) })
  });
  
  return t;
  
} , {p : Promise.resolve() , treated : [] , fulfilled : [] , rejected : [] } )


listed.p.then( function(){ 
  log( 'listed : ' + $j( listed , '   ' )) 
});

答案 2 :(得分:2)

在@jfriend00演示的方法旁边,您使用一个数组来解析每个promise,其中将当前值附加到所有先前的结果,您还可以使用一个promises数组,如并行执行模式中所知{{1 }和Promise.all

为此,您必须将您在.map步骤中创建的所有承诺放在数组中。之后,您可以在此阵列上调用reduce以等待所有结果。这种方法的优点是您的代码只需要很少的调整,因此您可以轻松地在需要结果的版本和不需要结果的版本之间来回切换。
要收集数组中每个步骤的结果,我们使用Promise.all的变体(称为reduce)并返回一个数组(如scan)而不是最新结果:< / p>

map

模式现在看起来像

Array.prototype.scan = function scanArray(callback, accumulator) {
    "use strict";
    if (this == null) throw new TypeError('Array::scan called on null or undefined');
    if (typeof callback !== 'function') throw new TypeError(callback+' is not a function');

    var arr = Object(this),
        len = arr.length >>> 0,
        res = [];
    for (var k = 0; k < len; k++)
        if (k in arr)
            res[k] = accumulator = callback(accumulator, arr[k], k, arr);
    return res;
};

(对于jQuery,请Promise.all(someArray.scan(function(p, item) { return p.then(function() { return someFunction(item); }); }, Promise.resolve())).then(…) 替换Promise.resolve$.Deferred().resolve()替换Promise.all