javascript - How to return accumulated returned Promise values as array to .then() following Array.prototype.reduce()? - Stack O

Given this patternsomeArray.reduce(function(p, item) {return p.then(function() {return someFunction(ite

Given this pattern

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() {

});

what approaches are possible to return accumulated values of fulfilled , rejected Promise objects to .then(fulfilled) as an array following call to .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=".11.1/jquery.min.js">
</script>

Given this pattern

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() {

});

what approaches are possible to return accumulated values of fulfilled , rejected Promise objects to .then(fulfilled) as an array following call to .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./ajax/libs/jquery/1.11.1/jquery.min.js">
</script>

Share Improve this question edited May 26, 2017 at 20:20 guest271314 asked Nov 13, 2015 at 8:18 guest271314guest271314 102k15 gold badges117 silver badges187 bronze badges 6
  • Can you clarify "accumulated values of fulfilled , rejected Promise objects", please? How should the result look exactly, and how do you want rejections to be treated? – Bergi Commented Nov 13, 2015 at 8:24
  • @Bergi A single array or object containing fulfilled Promise values at .then(fulfilled) , similarly at rejected .then(rejected) – guest271314 Commented Nov 13, 2015 at 8:40
  • What is "similarly"? Be specific, please. – Bergi Commented Nov 13, 2015 at 8:52
  • 1 maybe you should take a look at this answer – Anonymous0day Commented Nov 13, 2015 at 8:53
  • @Bergi "What is "similarly"? Be specific, please." Results can be either array of results or object having values as results. Both results - fulfilled , rejected - should be same type of data structure. – guest271314 Commented Nov 13, 2015 at 8:58
 |  Show 1 more ment

3 Answers 3

Reset to default 8

There are multiple possible strategies depending upon the specifics of what you're trying to do: Here's one option:

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
});

Working demo: http://jsfiddle/jfriend00/d4q1aaa0/


FYI, the Bluebird Promise library (which is what I generally use) has .mapSeries() which is built for this pattern:

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

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

Working demo: http://jsfiddle/jfriend00/7fm3wv7j/

One solution possible :

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 , '   ' )) 
});

Next to the approach demonstrated by @jfriend00, where you resolve each promise with an array where you append the current to all previous results, you can also use an array of promises as is known from the parallel execution pattern with Promise.all and .map.

For this, you have to put all the promises you create within the reduce steps in an array. After that, you can call Promise.all on this array to await all the results. The advantage of this approach is that your code only needs minimal adjustment, so that you can easily switch back and forth between a version that needs the results and one that does not.
To collect the results of each step in an array, we use a variant of reduce that is known as scan and does return an array (like map) instead of the latest result:

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;
};

The pattern now looks like

Promise.all(someArray.scan(function(p, item) {
    return p.then(function() {
       return someFunction(item);
    });
}, Promise.resolve())).then(…)

(For jQuery, substitute Promise.resolve by $.Deferred().resolve() and Promise.all by $.when.apply($, …))

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744354186a4570149.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信