Tengo una estructura similar a una matriz que expone métodos asíncronos. Las llamadas al método asíncrono devuelven estructuras de matriz que, a su vez, exponen más métodos asíncronos. Estoy creando otro objeto JSON para almacenar los valores obtenidos de esta estructura, por lo que debo tener cuidado al realizar un seguimiento de las referencias en las devoluciones de llamada.
He codificado una solución de fuerza bruta, pero me gustaría aprender una solución más idiomática o limpia.
Aquí hay un código parcial:
var jsonItems = []; items.forEach(function(item){ var jsonItem = {}; jsonItem.name = item.name; item.getThings().then(function(things){ // or Promise.all(allItemGetThingCalls, function(things){ things.forEach(function(thing, index){ jsonItems[index].thingName = thing.name; if(thing.type === 'file'){ thing.getFile().then(function(file){ //or promise.all? jsonItems[index].filesize = file.getSize();Es bastante sencillo con algunas reglas simples:
then , devuélvala : cualquier promesa que no devuelva no se esperará fuera..all ellas : de esa manera espera a que se silencien todas las promesas y ningún error de ninguna de ellas.then s, generalmente puede regresar en el medio ; then , las cadenas generalmente tienen como máximo 1 nivel de profundidad.Y algunos consejos:
.map que con for/push : si está asignando valores con una función, map le permite expresar de manera concisa la noción de aplicar acciones una por una y agregar los resultados.Promise.all que ejecutar las cosas una tras otra, cada una esperando antes de la siguiente.Bien, empecemos:
var items = [1, 2, 3, 4, 5]; var fn = function asyncMultiplyBy2(v){ // sample async action return new Promise(resolve => setTimeout(() => resolve(v * 2), 100)); }; // map over forEach since it returns var actions = items.map(fn); // run the function over all items // we now have a promises array and we want to wait for it var results = Promise.all(actions); // pass array of promises results.then(data => // or just .then(console.log) console.log(data) // [2, 4, 6, 8, 10] ); // we can nest this of course, as I said, `then` chains: var res2 = Promise.all([1, 2, 3, 4, 5].map(fn)).then( data => Promise.all(data.map(fn)) ).then(function(data){ // the next `then` is executed after the promise has returned from the previous // `then` fulfilled, in this case it's an aggregate promise because of // the `.all` return Promise.all(data.map(fn)); }).then(function(data){ // just for good measure return Promise.all(data.map(fn)); }); // now to get the results: res2.then(function(data){ console.log(data); // [16, 32, 48, 64, 80] });Aquí hay un ejemplo simple usando reduce. Se ejecuta en serie, mantiene el orden de inserción y no requiere Bluebird.
/** * * @param items An array of items. * @param fn A function that accepts an item from the array and returns a promise. * @returns {Promise} */ function forEachPromise(items, fn) { return items.reduce(function (promise, item) { return promise.then(function () { return fn(item); }); }, Promise.resolve()); }Y utilízalo así:
var items = ['a', 'b', 'c']; function logItem(item) { return new Promise((resolve, reject) => { process.nextTick(() => { console.log(item); resolve(); }) }); } forEachPromise(items, logItem).then(() => { console.log('done'); });Hemos encontrado útil enviar un contexto opcional en bucle. El contexto es opcional y compartido por todas las iteraciones.
function forEachPromise(items, fn, context) { return items.reduce(function (promise, item) { return promise.then(function () { return fn(item, context); }); }, Promise.resolve()); }Su función de promesa se vería así:
function logItem(item, context) { return new Promise((resolve, reject) => { process.nextTick(() => { console.log(item); context.itemCount++; resolve(); }) }); }Tuve a través de la misma situación. Lo resolví usando dos Promise.All().
Creo que fue una solución realmente buena, así que la publiqué en npm: https://www.npmjs.com/package/promise-foreach
Creo que tu código será algo como esto.
var promiseForeach = require('promise-foreach') var jsonItems = []; promiseForeach.each(jsonItems, [function (jsonItems){ return new Promise(function(resolve, reject){ if(jsonItems.type === 'file'){ jsonItems.getFile().then(function(file){ //or promise.all? resolve(file.getSize()) }) } }) }], function (result, current) { return { type: current.type, size: jsonItems.result[0] } }, function (err, newList) { if (err) { console.error(err) return; } console.log('new jsonItems : ', newList) })