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($, …)
)