J'ai un tableau comme la structure qui expose les méthodes asynchrones. Les appels de méthodes asynchrones renvoient des structures de tableau qui, à leur tour, exposent davantage de méthodes asynchrones. Je suis en train de créer un autre objet JSON pour stocker les valeurs obtenues à partir de cette structure. Je dois donc veiller à ne pas perdre la trace des références dans les rappels.
J'ai codé une solution de force brute, mais j'aimerais apprendre une solution plus idiomatique ou plus propre.
Voici un code partiel -
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();
C'est assez simple avec quelques règles simples:
then
, renvoyez-la - aucune promesse que vous ne revenez pas ne sera pas attendue à l'extérieur..all
leur - ainsi, toutes les promesses seront attendues et aucune erreur de l'une d'entre elles ne sera réduite au silence.then
s, vous pouvez généralement revenir au milie - then
les chaînes ont généralement une profondeur maximale de 1 niveau.Et quelques astuces:
.map
que avec for/Push
- si vous mappez des valeurs avec une fonction, map
vous permet d'exprimer de manière concise la notion d'appliquer des actions par un et agréger les résultats.Promise.all
plutôt que d'exécuter les choses les unes après les autres - chaque attente avant la suivante.Ok, alors commençons:
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]
});
Voici un exemple simple en utilisant réduire. Il fonctionne en série, maintient l'ordre d'insertion et ne requiert pas 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());
}
Et utilisez-le comme ceci:
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');
});
Nous avons trouvé utile d'envoyer un contexte optionnel dans loop. Le contexte est optionnel et partagé par toutes les itérations.
function forEachPromise(items, fn, context) {
return items.reduce(function (promise, item) {
return promise.then(function () {
return fn(item, context);
});
}, Promise.resolve());
}
Votre fonction de promesse ressemblerait à ceci:
function logItem(item, context) {
return new Promise((resolve, reject) => {
process.nextTick(() => {
console.log(item);
context.itemCount++;
resolve();
})
});
}
J'ai traversé la même situation. J'ai résolu en utilisant deux Promise.All ().
Je pense que c’était vraiment une bonne solution, je l’ai donc publiée sur npm: https://www.npmjs.com/package/promise-foreach
Je pense que votre code sera quelque chose comme ça
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)
})