Ho una matrice come struttura che espone metodi asincroni. Il metodo async chiama le strutture di array di restituzione che a loro volta espongono metodi più asincroni. Sto creando un altro object JSON per memorizzare i valori ottenuti da questa struttura e quindi devo fare attenzione a tenere traccia dei riferimenti nei callback.
Ho codificato una soluzione di forza bruta, ma mi piacerebbe imparare una soluzione più idiomatica o pulita.
Ecco un codice parziale –
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();
È piuttosto semplice con alcune semplici regole:
then
, restituiscilo : qualsiasi promise che non torni non sarà attesa fuori. then
s, in genere puoi tornare al centro – then
catene sono solitamente al massimo a 1 livello di profondità. E alcuni consigli:
.map
che con for/push
– se stai mappando i valori con una funzione, map
ti consente di esprimere in modo conciso la nozione di applicare le azioni una ad una e di aggregare i risultati. Promise.all
che eseguire le cose una dopo l’altra – ciascuna in attesa prima della successiva. Ok, allora iniziamo:
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] });
Ecco un semplice esempio usando ridurre. Funziona in serie, mantiene l’ordine di inserimento e non richiede 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()); }
E usalo in questo modo:
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'); });
Abbiamo trovato utile inviare un contesto opzionale in loop. Il contesto è facoltativo e condiviso da tutte le iterazioni.
function forEachPromise(items, fn, context) { return items.reduce(function (promise, item) { return promise.then(function () { return fn(item, context); }); }, Promise.resolve()); }
La tua funzione di promise sarebbe simile a questa:
function logItem(item, context) { return new Promise((resolve, reject) => { process.nextTick(() => { console.log(item); context.itemCount++; resolve(); }) }); }
Ho avuto la stessa situazione. Ho risolto utilizzando due Promise.All ().
Penso che sia stata una buona soluzione, quindi l’ho pubblicata su npm: https://www.npmjs.com/package/promise-foreach
Penso che il tuo codice sarà qualcosa del genere
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) })