Il semble y avoir des problèmes lors de l’incorporation de async/wait avec .reduce (), comme ceci:
const data = await bodies.reduce(async(accum, current, index) => {
const methodName = methods[index]
const method = this[methodName]
if (methodName == 'foo') {
current.cover = await this.store(current.cover, id)
console.log(current)
return {
...accum,
...current
}
}
return {
...accum,
...method(current.data)
}
}, {})
console.log(data)
L'objet data
est enregistré avant le this.store
Se termine ...
Je sais que vous pouvez utiliser Promise.all
Avec des boucles asynchrones, mais cela s'applique-t-il à .reduce()
?
Le problème est que vos valeurs d'accumulateur sont des promesses - elles renvoient des valeurs de async function
s. Pour obtenir une évaluation séquentielle (et même attendre la dernière itération), vous devez utiliser
const data = await array.reduce(async (accumP, current, index) => {
const accum = await accumP;
…
}, Promise.resolve(…));
Cela dit, pour async
/await
, je recommanderais en général de tiliser des boucles simples à la place des méthodes d'itération de tableaux , elles sont plus performantes et souvent plus simples.
J'aime la réponse de Bergi, je pense que c'est la bonne façon de faire.
J'aimerais aussi mentionner une de mes bibliothèques, appelée Awaity.js
Ce qui vous permet d’utiliser facilement des fonctions telles que reduce
, map
& filter
avec async / await
:
import reduce from 'awaity/reduce';
const posts = await reduce([1,2,3], async (posts, id) => {
const res = await fetch('/api/posts/' + id);
const post = await res.json();
return {
...posts,
[id]: post
};
}, {})
posts // { 1: { ... }, 2: { ... }, 3: { ... } }
Vous pouvez envelopper l'ensemble de votre carte/réduire le nombre de blocs d'itérateur dans leur propre Promise.resolve et attendre que cette opération soit terminée. Le problème, cependant, est que l'accumulateur ne contient pas les données/objets que vous attendez à chaque itération. En raison de la chaîne asynchrone/wait/Promise interne, l'accumulateur sera de véritables promesses qui doivent encore se résoudre malgré l'utilisation d'un mot clé wait avant votre appel au magasin (ce qui pourrait vous amener à penser que l'itération ne revenir jusqu'à ce que l'appel soit terminé et que l'accumulateur soit mis à jour.
Bien que ce ne soit pas la solution la plus élégante, vous avez la possibilité de déplacer votre variable d'objet data hors de la portée et de l'affecter sous la forme d'une let afin qu'une liaison et une mutation appropriées puissent se produire. Ensuite, mettez à jour cet objet de données à l'intérieur de votre itérateur lors de la résolution des appels async/wait/Promise.
/* allow the result object to be initialized outside of scope
rather than trying to spread results into your accumulator on iterations,
else your results will not be maintained as expected within the
internal async/await/Promise chain.
*/
let data = {};
await Promise.resolve(bodies.reduce(async(accum, current, index) => {
const methodName = methods[index]
const method = this[methodName];
if (methodName == 'foo') {
// note: this extra Promise.resolve may not be entirely necessary
const cover = await Promise.resolve(this.store(current.cover, id));
current.cover = cover;
console.log(current);
data = {
...data,
...current,
};
return data;
}
data = {
...data,
...method(current.data)
};
return data;
}, {});
console.log(data);
export const addMultiTextData = async(data) => {
const textData = await data.reduce(async(a, {
currentObject,
selectedValue
}) => {
const {
error,
errorMessage
} = await validate(selectedValue, currentObject);
return {
...await a,
[currentObject.id]: {
text: selectedValue,
error,
errorMessage
}
};
}, {});
};