À l'aide de async/await avec une boucle forEach
Existe-il des problèmes avec l'utilisation de async
/await
dans un forEach
boucle? Je suis en train de parcourir un tableau de fichiers et await
sur le contenu de chaque fichier.
import fs from 'fs-promise'
async function printFiles () {
const files = await getFilePaths() //Assume this works fine
files.forEach(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
})
}
printFiles()
Ce code fonctionne, mais quelque chose pourrait aller mal avec cela? J'ai eu quelqu'un me dire que vous n'êtes pas censé utiliser async
/await
dans un ordre supérieur de la fonction comme cela, alors je voulais juste vous demander si il n'y avait aucun problème avec cela.
Vous devez vous connecter pour publier un commentaire.
Vous assurer que le code fonctionne, mais je suis sûr qu'on ne fait pas ce que vous attendez pour le faire. Il a juste déclenche de multiples appels asynchrones, mais le
printFiles
fonction ne revenez immédiatement après.Si vous souhaitez lire les fichiers en séquence, vous ne pouvez pas utiliser
forEach
en effet. Il suffit d'utiliser un modernefor … of
boucle au lieu de cela, en ce quiawait
fonctionne comme prévu:Si vous souhaitez lire les fichiers en parallèle, vous ne pouvez pas utiliser
forEach
en effet. Chacun desasync
rappel des appels de fonction retourne une promesse, mais vous êtes les jeter à la poubelle au lieu de les attendent. Utilisez simplementmap
au lieu de cela, et vous pouvez attendre la matrice de promesses que vous aurez avecPromise.all
:for ... of ...
travail?async
/await
de générateur de fonction et l'utilisation deforEach
signifie que chaque itération dispose d'un générateur de fonction, qui n'a rien à voir avec les autres. donc, ils vont être exécutés de manière indépendante et n'a pas de contexte denext()
avec les autres. En fait, un simplefor()
boucle fonctionne aussi parce que les itérations sont également en un seul générateur de fonction.await
suspend l'actuel function de l'évaluation, y compris toutes les structures de contrôle. Oui, il est tout à fait semblables à des générateurs à cet égard (c'est pourquoi elles sont utilisées pour polyfill async/await).files.map(async (file) => ...
est équivalent àfiles.map((file) => new Promise((rej, res) => { ...
?async
fonction est tout à fait différent d'unPromise
exécuteur de rappel, mais oui, lesmap
de rappel renvoie une promesse dans les deux cas.await
dans quel camp nefor( in )
résident? les pouces vers le bas avecforEach
ou pouces avecfor of
??await
, mais vous avez rarement besoin d'énumérer les propriétés de l'objet.await Promise.all(_.map(arr, async (val) => {...});
résolu mon problème. Bien sûr, chaque async callback retourne une promesse que je n'étais pas en attente sur.for..of
" ? Parce que vous pouvez lire les fichiers en parallèle avec laforEach
, c'est juste qu'il sera très laid parce que vous ne pouvez pasawait
il.for…of
qui fonctionne aussi pourforEach
. Non, je veux vraiment dire que le paragraphe pour souligner qu'il n'y a pas de place pour.forEach
moderne du code JS.map
ouforEach
, les requêtes s'exécutent en parallèle.for..of
est mauvais pour la performance en comparaison àforEach
/map
, parce quefor..of
arrêt à chaque itération, donc, si je fais 3 sur demande, chaque demande devra attendre pour les précédentes requêtes. Mais avecforEach
/map
, toutes les demandes seront faites en parallèle.queries.forEach(q => asyncRequest(q));
fait exactement la même chose quefor (const q of queries) asyncRequest(q);
. Il n'y a aucune différence dans les performances, et les deux vont exécuter les requêtes en parallèle. Bien sûr, dans les deux vous pouvez attendre pour rien - pour savoir comment procéder, voir ma réponse.forEach
. Où est le problème?forEach
(fonctionne de la même commemap
concernant le temps d'exécution). Mais comme vous pouvez le voir dans mon précédent commentaire, je parlais soitforEach
oumap
contrefor..of
. J'ai dit: pour..de l'est mauvais pour la performance en comparaison à forEach/map.map
exemple, sauf avecforEach
il sera moche et impropre à uneasync
/await
.for…of
n'est pas mauvais pour la performance en comparaison àforEach
si elle est utilisée de la même façon impropre, sans attendre quoi que ce soit. Et oui, "ne peut pas être utilisé" signifie "est impropre".await
quelle que soit la demande que vous faites à l'intérieur d'unfor..of
? Si oui, ok, mais vous n'acceptez que votre deuxième exemple est beaucoup plus performant que votre premier exemple ?forEach
oufor…of
. Et non, je ne voudrais pas comparer les deux (for…of
+await
vsPromise.all
+map
) en termes de "performance" à tous - ils sont juste différents, dans de nombreux autres plus importantes ce qui concerne. Le choix séquentiel vs parallèle de l'exécution est une décision qui implique de nombreux autres facteurs, et bien sûr en parallèle généralement termine plus vite.for..of
peut-être la seule façon d'aller de l'e.g si chaque demande a une dépendance sur la demande précédente), mais un débutant pourrait opter pour lafor..of
solution parce que c'est plus simple.forEach
est absolument inapte comme vous l'avez dit vous-même.forEach
.forEach
, c'est tout ce qu'ils doivent savoir.var string = (await Promise.all(…)).join(…)
.const
déclarations fonctionnent parfaitement bien dans les boucles. Les variables est jamais réattribué.foreach
avecasync
ne fonctionne pas. Depuis, nous avons spécifiéawait
à l'intérieur de la boucle, il doit attendre la réponse?async function
appel attend et retourne une promesse.forEach
ne se soucient pas de cette promesse.async
/await
syntaxe et retourne juste la promesse d'map()
directement? Je comprends que le moment deconsole.log()
n'allait plus être la même, mais il semble être la confusion sur le fait queasync
n'est-ce pas en quelque sorte nécessaires à l'utilisation dePromise.all()
etmap()
.var promises = []; array.forEach(value => { promises.push(someAsync(value)); }); return Promise.all(promises);
parce que je vois beaucoup de personnes, ou des questions similaires avec de meilleures réponses, mais le code est trop compliqué pour être réutilisables comme un double de la cible.Avec ES2018, vous êtes en mesure de simplifier grandement toutes les réponses ci-dessus à:
Voir spec: https://github.com/tc39/proposal-async-iteration
2018-09-10: Cette réponse a été l'objet de beaucoup d'attention récemment, voir Axel Rauschmayer du blog pour plus d'informations sur asynchrones itération: http://2ality.com/2016/10/asynchronous-iteration.html
file
définis dans votre code?file
est défini. C'est porté de 1:1 à partir de la question ci-dessus.of
devrait être l'asynchrone en fonction retournera un tableau. Il ne fonctionne pas et a dit Francisco;for-await-of
avec un synchrones itérable (un tableau dans notre cas) ne couvre pas le cas de l'itération simultanément un tableau à l'aide des opérations asynchrones à chaque itération. Si je ne me trompe pas, à l'aide defor-await-of
avec un synchrones itératif sur les non-promesse de des valeurs est le même qu'en utilisant un simplefor-of
.files
tableau à lafs.readFile
ici? Il y de itératif?Au lieu de
Promise.all
en conjonction avecArray.prototype.map
(ce qui ne garantit pas l'ordre dans lequel lesPromise
s sont résolu), j'utiliseArray.prototype.reduce
, en commençant avec une résoluPromise
:Promise.resolve()
etawait promise;
?Promise
objet, de sorte quereduce
a unPromise
pour commencer.await promise;
va attendre le dernierPromise
dans la chaîne afin de les résoudre. @GollyJer Les fichiers seront traitées de manière séquentielle, un à la fois.La p-itération module npm met en œuvre la Matrice d'itération méthodes afin qu'ils puissent être utilisés d'une manière très simple avec async/await.
Un exemple avec votre cas:
some
plutôt queforEach
. Merci!Voici quelques
forEachAsync
prototypes. Remarque: vous aurez besoin deawait
eux:Note alors que vous pouvez inclure dans votre propre code, vous ne devez pas inclure cela dans les bibliothèques que vous les distribuer aux autres (pour éviter de polluer leur globals).
_forEachAsync
) ce qui est raisonnable. Je pense aussi que c'est la plus belle réponse qu'il permet d'économiser beaucoup de code réutilisable.globals.js
serait bien), on peut ajouter des variables globales comme on les aime.npm audit
ici et là. Mon point était de ne pas recommander l'approche énumérées dans la réponse à une personne qui n'est pas pleinement conscients des conséquences, en particulier en 2018.Les deux solutions ci-dessus ne fonctionne, cependant, Antonio fait le travail avec moins de code, voici comment il m'a aidé à résoudre des données de ma base de données, à partir de plusieurs différent de l'enfant refs et puis en poussant tous dans un tableau et de le résoudre en une promesse, après tout, est fait:
c'est assez indolore de la pop un couple de méthodes dans un fichier qui va gérer les données asynchrone dans un sérialisée de l'ordre et de donner une plus classique de la saveur à votre code. Par exemple:
maintenant, en supposant que c'est enregistré à". /myAsync.js " vous pouvez faire quelque chose de similaire à ci-dessous à côté d'un fichier:
Un important mise en garde est: La
await + for .. of
méthode et laforEach + async
façon en fait avoir un effet différent.Avoir
await
l'intérieur d'un vraifor
boucle assurez-vous que tous les appels asynchrones sont exécutées une par une. Et leforEach + async
façon se déclenche toutes les promesses dans le même temps, ce qui est plus rapide mais parfois débordé(si vous faites quelques DB de requête ou visiter quelques-uns des services web avec les restrictions de volume et ne veulent pas de feu de 100 000 appels à un moment).Vous pouvez également utiliser
reduce + promise
(moins élégant) si vous n'utilisez pasasync/await
et veulent s'assurer que les fichiers sont en lecture l'un après l'autre.Ou vous pouvez créer un forEachAsync pour les aider, mais, fondamentalement, utiliser le même pour la boucle sous-jacent.
forEach
- pour accéder à des indices au lieu de compter sur iterability - et passer l'indice de la fonction de rappel.Array.prototype.reduce
d'une manière qui utilise une fonction async. J'ai montré un exemple dans ma réponse: stackoverflow.com/a/49499491/2537258À l'aide de la Tâche, futurize, et un traversable Liste, vous pouvez simplement faire
Ici est de savoir comment vous définissez ce
Un autre moyen d'avoir structuré le code désiré serait
Ou peut-être même plus fonctionnellement orientée
Puis à partir de la fonction parent
Si vous voulais vraiment plus de souplesse dans l'encodage, vous pouvez simplement le faire (pour le fun, je suis en utilisant le projet de Pipe Avant opérateur )
PS - je n'ai pas essayé ce code sur la console, pourrait avoir des fautes de frappe... "straight freestyle, hors de la partie supérieure de la coupole!" que les années 90, des enfants me le dire. :-p
En plus de @Bergi réponse, je tiens à offrir une troisième alternative. Il est très similaire à @Bergi 2e exemple, mais au lieu de l'attente de chaque
readFile
individuellement, vous créez un tableau de promesses, chaque qui vous attendent à la fin.Noter que la fonction transmise à
.map()
n'a pas besoin d'êtreasync
, depuisfs.readFile
retourne une Promesse d'objets de toute façon. Doncpromises
est un tableau de la Promesse d'objets, qui peuvent être envoyés àPromise.all()
.Dans @Bergi réponse, la console peut enregistrer le contenu du fichier de commande. Par exemple, si un très petit fichier terminé la lecture devant un très gros fichier, il sera enregistré en premier, même si le fichier de petite taille, vient après le fichier de grande taille dans le
files
tableau. Cependant, dans ma méthode ci-dessus, vous avez la garantie de la console enregistre les fichiers dans le même ordre qu'ils sont lus.Actuellement le Tableau.forEach propriété prototype ne prend pas en charge des opérations asynchrones, mais nous pouvons créer notre propre poly-remplir pour répondre à nos besoins.
Et c'est tout! Vous avez maintenant un async forEach méthode disponible sur tous les tableaux qui sont définies à l'issue de ces opérations.
Nous allons tester...
Nous pourrions faire de même pour certaines autres fonctions de tableau comme carte...
... et ainsi de suite 🙂
Des choses à noter:
Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>
ne disposent pas de cette fonctionnalité disponibleSimilaire à Antonio Val
p-itération
, une alternative mnp module estasync-af
:Sinon,
async-af
possède une méthode statique (log/logAF) qui enregistre les résultats de promesses:Cependant, le principal avantage de la bibliothèque, c'est que vous pouvez enchaîner les méthodes asynchrones pour faire quelque chose comme:
async-af
Bergi de la solution fonctionne très bien quand
fs
est la promesse base.Vous pouvez utiliser
bluebird
,fs-extra
oufs-promise
pour cela.Cependant, la solution pour nœud natif
fs
bibliothèque est comme suit:Remarque:
require('fs')
obligatoirement prend fonction en tant que 3e arguments, sinon déclenche une erreur:Je voudrais utiliser le bien testé (en millions de téléchargements par semaine) pify et async modules. Si vous n'êtes pas familier avec la async module, je vous recommande fortement de vérifier ses docs. J'ai vu plusieurs devs perdre du temps à recréer ses méthodes, ou pire, de décisions difficiles à maintenir async code lors d'ordre supérieur de méthodes asynchrones serait de simplifier le code.