with objects initial empty ejemplo array javascript arrays promise jquery-deferred

javascript - objects - reduce of empty array with no initial value



¿Cómo devolver los valores Promise devueltos acumulados como una matriz a.then() después de Array.prototype.reduce()? (3)

Dado este patrón

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

¿Qué enfoques son posibles para devolver los valores acumulados de los objetos Promise cumplidos y rechazados a .then(fulfilled) como una matriz después de la llamada a .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>


Existen múltiples estrategias posibles que dependen de los detalles de lo que intenta hacer: esta es una opción:

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

Demostración de trabajo: http://jsfiddle.net/jfriend00/d4q1aaa/

FYI, la biblioteca Bluebird Promise (que es lo que generalmente uso) tiene .mapSeries() que está construida para este patrón:

var someArray = [1,2,3,4]; Promise.mapSeries(someArray, function(item) { return someFunction(item); }).then(function(results) { log(results); });

Demostración de trabajo: http://jsfiddle.net/jfriend00/7fm3wv7j/


Una solución posible:

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


Junto al enfoque demostrado por @ jfriend00, donde puedes resolver cada promesa con una matriz donde anexas la corriente a todos los resultados previos, también puedes usar una serie de promesas como se conoce en el patrón de ejecución paralela con Promise.all y .map .

Para esto, debe poner todas las promesas que cree dentro de los pasos de reduce en una matriz. Después de eso, puede llamar a Promise.all en esta matriz para esperar todos los resultados. La ventaja de este enfoque es que su código solo necesita un ajuste mínimo, por lo que puede cambiar fácilmente entre una versión que necesita los resultados y otra que no.
Para recopilar los resultados de cada paso en una matriz, usamos una variante de reduce que se conoce como scan y devuelve una matriz (como un map ) en lugar del último resultado:

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

El patrón ahora se ve como

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

(Para jQuery, sustituya Promise.resolve por $.Deferred().resolve() Promise.all $.Deferred().resolve() y Promise.all por $.when.apply($, …) )