Utiliser async / await avec une boucle forEach
Y a-t-il des problèmes avec l'utilisation de async
/ await
dans une forEach
boucle? J'essaye 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 y a-t-il un problème avec cela? Quelqu'un m'a dit que vous n'êtes pas censé utiliser async
/ await
dans une fonction d'ordre supérieur comme celle-ci, donc je voulais juste demander s'il y avait un problème avec cela.
Réponses
Bien sûr, le code fonctionne, mais je suis à peu près sûr qu'il ne fait pas ce que vous attendez de lui. Il déclenche simplement plusieurs appels asynchrones, mais la printFiles
fonction revient immédiatement après cela.
Lecture en séquence
Si vous souhaitez lire les fichiers dans l'ordre, vous ne pouvez pas enforEach
effet utiliser . Utilisez simplement une for … of
boucle moderne à la place, dans laquelle await
fonctionnera comme prévu:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
Lecture en parallèle
Si vous souhaitez lire les fichiers en parallèle, vous ne pouvez enforEach
effet pas utiliser . Chacun des async
appels de fonction de rappel renvoie une promesse, mais vous les jetez au lieu de les attendre. Utilisez simplement à la map
place, et vous pouvez attendre le tableau de promesses que vous obtiendrez Promise.all
:
async function printFiles () {
const files = await getFilePaths();
await Promise.all(files.map(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
}));
}
Avec ES2018, vous êtes en mesure de simplifier considérablement toutes les réponses ci-dessus pour:
async function printFiles () {
const files = await getFilePaths()
for await (const contents of fs.readFile(file, 'utf8')) {
console.log(contents)
}
}
Voir la spécification: proposition-async-itération
2018-09-10: Cette réponse a suscité beaucoup d'attention récemment, veuillez consulter l'article de blog d'Axel Rauschmayer pour plus d'informations sur l'itération asynchrone: ES2018: itération asynchrone
Au lieu de Promise.all
conjointement avec Array.prototype.map
(qui ne garantit pas l'ordre dans lequel les Promise
s sont résolus), j'utilise Array.prototype.reduce
, en commençant par un résolu Promise
:
async function printFiles () {
const files = await getFilePaths();
await files.reduce(async (promise, file) => {
// This line will wait for the last async function to finish.
// The first iteration uses an already resolved Promise
// so, it will immediately continue.
await promise;
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}, Promise.resolve());
}
Le module p-iteration sur npm implémente les méthodes d'itération Array afin qu'elles puissent être utilisées de manière très simple avec async / await.
Un exemple avec votre cas:
const { forEach } = require('p-iteration');
const fs = require('fs-promise');
(async function printFiles () {
const files = await getFilePaths();
await forEach(files, async (file) => {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
});
})();
Voici quelques forEachAsync
prototypes. Notez que vous en aurez besoin await
:
Array.prototype.forEachAsync = async function (fn) {
for (let t of this) { await fn(t) }
}
Array.prototype.forEachAsyncParallel = async function (fn) {
await Promise.all(this.map(fn));
}
Notez que bien que vous puissiez l'inclure dans votre propre code, vous ne devez pas l'inclure dans les bibliothèques que vous distribuez à d'autres (pour éviter de polluer leurs globaux).
En plus de la réponse de @ Bergi , j'aimerais proposer une troisième alternative. C'est très similaire au deuxième exemple de @ Bergi, mais au lieu d'attendre chacune readFile
individuellement, vous créez un tableau de promesses, chacune que vous attendez à la fin.
import fs from 'fs-promise';
async function printFiles () {
const files = await getFilePaths();
const promises = files.map((file) => fs.readFile(file, 'utf8'))
const contents = await Promise.all(promises)
contents.forEach(console.log);
}
Notez que la fonction passée à .map()
n'a pas besoin de l'être async
, car fs.readFile
renvoie de toute façon un objet Promise. Par conséquent, promises
un tableau d'objets Promise, qui peuvent être envoyés à Promise.all()
.
Dans la réponse de @ Bergi, la console peut enregistrer le contenu des fichiers dans l'ordre dans lequel ils sont lus. Par exemple, si un très petit fichier finit de lire avant un très gros fichier, il sera enregistré en premier, même si le petit fichier vient après le gros fichier du files
tableau. Cependant, dans ma méthode ci-dessus, vous êtes assuré que la console enregistrera les fichiers dans le même ordre que le tableau fourni.
La solution de Bergi fonctionne bien lorsqu'elle fs
est basée sur la promesse. Vous pouvez utiliser bluebird
, fs-extra
ou fs-promise
pour cela.
Cependant, la solution pour la fs
bibliothèque native de node est la suivante:
const result = await Promise.all(filePaths
.map( async filePath => {
const fileContents = await getAssetFromCache(filePath, async function() {
// 1. Wrap with Promise
// 2. Return the result of the Promise
return await new Promise((res, rej) => {
fs.readFile(filePath, 'utf8', function(err, data) {
if (data) {
res(data);
}
});
});
});
return fileContents;
}));
Remarque:
require('fs')
prend obligatoirement la fonction comme troisième argument, sinon génère une erreur:
TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
Les deux solutions ci-dessus fonctionnent, cependant, Antonio fait le travail avec moins de code, voici comment cela m'a aidé à résoudre les données de ma base de données, de plusieurs refs enfants différents, puis à les pousser tous dans un tableau et à les résoudre dans une promesse après tout est terminé:
Promise.all(PacksList.map((pack)=>{
return fireBaseRef.child(pack.folderPath).once('value',(snap)=>{
snap.forEach( childSnap => {
const file = childSnap.val()
file.id = childSnap.key;
allItems.push( file )
})
})
})).then(()=>store.dispatch( actions.allMockupItems(allItems)))
il est assez facile d'insérer quelques méthodes dans un fichier qui gérera les données asynchrones dans un ordre sérialisé et donnera une saveur plus conventionnelle à votre code. Par exemple:
module.exports = function () {
var self = this;
this.each = async (items, fn) => {
if (items && items.length) {
await Promise.all(
items.map(async (item) => {
await fn(item);
}));
}
};
this.reduce = async (items, fn, initialValue) => {
await self.each(
items, async (item) => {
initialValue = await fn(initialValue, item);
});
return initialValue;
};
};
maintenant, en supposant qu'il soit enregistré dans './myAsync.js', vous pouvez faire quelque chose de similaire à ce qui suit dans un fichier adjacent:
...
/* your server setup here */
...
var MyAsync = require('./myAsync');
var Cat = require('./models/Cat');
var Doje = require('./models/Doje');
var example = async () => {
var myAsync = new MyAsync();
var doje = await Doje.findOne({ name: 'Doje', noises: [] }).save();
var cleanParams = [];
// FOR EACH EXAMPLE
await myAsync.each(['bork', 'concern', 'heck'],
async (elem) => {
if (elem !== 'heck') {
await doje.update({ $push: { 'noises': elem }});
}
});
var cat = await Cat.findOne({ name: 'Nyan' });
// REDUCE EXAMPLE
var friendsOfNyanCat = await myAsync.reduce(cat.friends,
async (catArray, friendId) => {
var friend = await Friend.findById(friendId);
if (friend.name !== 'Long cat') {
catArray.push(friend.name);
}
}, []);
// Assuming Long Cat was a friend of Nyan Cat...
assert(friendsOfNyanCat.length === (cat.friends.length - 1));
}
Cette solution est également optimisée en mémoire afin que vous puissiez l'exécuter sur 10 000 éléments de données et requêtes. Certaines des autres solutions ici planteront le serveur sur de grands ensembles de données.
Dans TypeScript:
export async function asyncForEach<T>(array: Array<T>, callback: (item: T, index: number) => void) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index);
}
}
Comment utiliser?
await asyncForEach(receipts, async (eachItem) => {
await ...
})
Une mise en garde importante est la suivante: la await + for .. of
méthode et la forEach + async
manière ont en fait un effet différent.
Avoir à l' await
intérieur d'une for
boucle réelle garantira que tous les appels asynchrones sont exécutés un par un. Et le forEach + async
chemin déclenchera toutes les promesses en même temps, ce qui est plus rapide mais parfois débordé ( si vous effectuez une requête DB ou visitez certains services Web avec des restrictions de volume et ne voulez pas lancer 100000 appels à la fois).
Vous pouvez également utiliser reduce + promise
(moins élégant) si vous ne l'utilisez pas async/await
et souhaitez vous assurer que les fichiers sont lus les uns après les autres .
files.reduce((lastPromise, file) =>
lastPromise.then(() =>
fs.readFile(file, 'utf8')
), Promise.resolve()
)
Ou vous pouvez créer un forEachAsync pour aider, mais utiliser essentiellement la même boucle for sous-jacente.
Array.prototype.forEachAsync = async function(cb){
for(let x of this){
await cb(x);
}
}
Juste ajouter à la réponse originale
- La syntaxe de lecture parallèle dans la réponse originale est parfois déroutante et difficile à lire, peut-être pouvons-nous l'écrire dans une approche différente
async function printFiles() {
const files = await getFilePaths();
const fileReadPromises = [];
const readAndLogFile = async filePath => {
const contents = await fs.readFile(file, "utf8");
console.log(contents);
return contents;
};
files.forEach(file => {
fileReadPromises.push(readAndLogFile(file));
});
await Promise.all(fileReadPromises);
}
- Pour un fonctionnement séquentiel, pas seulement pour ... of , la boucle for normale fonctionnera également
async function printFiles() {
const files = await getFilePaths();
for (let i = 0; i < files.length; i++) {
const file = files[i];
const contents = await fs.readFile(file, "utf8");
console.log(contents);
}
}
Comme la réponse de @ Bergi, mais avec une différence.
Promise.all
rejette toutes les promesses si on est rejeté.
Alors, utilisez une récursivité.
const readFilesQueue = async (files, index = 0) {
const contents = await fs.readFile(files[index], 'utf8')
console.log(contents)
return files.length <= index
? readFilesQueue(files, ++index)
: files
}
const printFiles async = () => {
const files = await getFilePaths();
const printContents = await readFilesQueue(files)
return printContents
}
printFiles()
PS
readFilesQueue
est en dehors de la printFiles
cause de l'effet secondaire * introduit par console.log
, il vaut mieux se moquer, tester et / ou espionner donc, ce n'est pas cool d'avoir une fonction qui renvoie le contenu (note latérale).
Par conséquent, le code peut simplement être conçu par cela: trois fonctions séparées qui sont «pures» ** et n'introduisent aucun effet secondaire, traitent la liste entière et peuvent facilement être modifiées pour gérer les cas d'échec.
const files = await getFilesPath()
const printFile = async (file) => {
const content = await fs.readFile(file, 'utf8')
console.log(content)
}
const readFiles = async = (files, index = 0) => {
await printFile(files[index])
return files.lengh <= index
? readFiles(files, ++index)
: files
}
readFiles(files)
Modification future / état actuel
Node prend en charge l'attente de niveau supérieur (cela n'a pas encore de plugin, ne l'aura pas et peut être activé via des drapeaux d'harmonie), c'est cool mais ne résout pas un problème (stratégiquement, je ne travaille que sur les versions LTS). Comment récupérer les fichiers?
Utilisation de la composition. Compte tenu du code, cela me donne l'impression que c'est à l'intérieur d'un module, donc, devrait avoir une fonction pour le faire. Sinon, vous devriez utiliser un IIFE pour envelopper le code de rôle dans une fonction asynchrone créant un module simple qui fait tout pour vous, ou vous pouvez aller dans le bon sens, il y a la composition.
// more complex version with IIFE to a single module
(async (files) => readFiles(await files())(getFilesPath)
Notez que le nom de la variable change en raison de la sémantique. Vous passez un foncteur (une fonction qui peut être invoquée par une autre fonction) et reçoit un pointeur sur la mémoire qui contient le bloc initial de logique de l'application.
Mais si ce n'est pas un module et que vous devez exporter la logique?
Enveloppez les fonctions dans une fonction asynchrone.
export const readFilesQueue = async () => {
// ... to code goes here
}
Ou changez les noms des variables, peu importe ...
*
par effet secondaire menans tout effet colactérien de l'application qui peut changer l'état / le comportement ou introduire des bogues dans l'application, comme IO.
**
par "pur", c'est en apostrophe puisque les fonctions ne sont pas pures et le code peut être convergé vers une version pure, quand il n'y a pas de sortie console, seulement des manipulations de données.
En plus de cela, pour être pur, vous devrez travailler avec des monades qui gèrent l'effet secondaire, qui sont sujettes aux erreurs, et traitent cette erreur séparément de l'application.
En utilisant Task, futurize et une liste traversable, vous pouvez simplement faire
async function printFiles() {
const files = await getFiles();
List(files).traverse( Task.of, f => readFile( f, 'utf-8'))
.fork( console.error, console.log)
}
Voici comment vous configurez cela
import fs from 'fs';
import { futurize } from 'futurize';
import Task from 'data.task';
import { List } from 'immutable-ext';
const future = futurizeP(Task)
const readFile = future(fs.readFile)
Une autre façon d'avoir structuré le code souhaité serait
const printFiles = files =>
List(files).traverse( Task.of, fn => readFile( fn, 'utf-8'))
.fork( console.error, console.log)
Ou peut-être même plus orienté fonctionnellement
// 90% of encodings are utf-8, making that use case super easy is prudent
// handy-library.js
export const readFile = f =>
future(fs.readFile)( f, 'utf-8' )
export const arrayToTaskList = list => taskFn =>
List(files).traverse( Task.of, taskFn )
export const readFiles = files =>
arrayToTaskList( files, readFile )
export const printFiles = files =>
readFiles(files).fork( console.error, console.log)
Puis à partir de la fonction parent
async function main() {
/* awesome code with side-effects before */
printFiles( await getFiles() );
/* awesome code with side-effects after */
}
Si vous voulez vraiment plus de flexibilité dans l'encodage, vous pouvez simplement le faire (pour le plaisir, j'utilise l' opérateur Pipe Forward proposé )
import { curry, flip } from 'ramda'
export const readFile = fs.readFile
|> future,
|> curry,
|> flip
export const readFileUtf8 = readFile('utf-8')
PS - Je n'ai pas essayé ce code sur la console, j'ai peut-être des fautes de frappe ... "Straight freestyle, off the top of the dome!" comme diraient les enfants des années 90. :-p
Actuellement, la propriété prototype Array.forEach ne prend pas en charge les opérations asynchrones, mais nous pouvons créer notre propre poly-fill pour répondre à nos besoins.
// Example of asyncForEach Array poly-fill for NodeJs
// file: asyncForEach.js
// Define asynForEach function
async function asyncForEach(iteratorFunction){
let indexer = 0
for(let data of this){
await iteratorFunction(data, indexer)
indexer++
}
}
// Append it as an Array prototype property
Array.prototype.asyncForEach = asyncForEach
module.exports = {Array}
Et c'est tout! Vous disposez maintenant d'une méthode async forEach disponible sur tous les tableaux définis après ces opérations.
Testons-le ...
// Nodejs style
// file: someOtherFile.js
const readline = require('readline')
Array = require('./asyncForEach').Array
const log = console.log
// Create a stream interface
function createReader(options={prompt: '>'}){
return readline.createInterface({
input: process.stdin
,output: process.stdout
,prompt: options.prompt !== undefined ? options.prompt : '>'
})
}
// Create a cli stream reader
async function getUserIn(question, options={prompt:'>'}){
log(question)
let reader = createReader(options)
return new Promise((res)=>{
reader.on('line', (answer)=>{
process.stdout.cursorTo(0, 0)
process.stdout.clearScreenDown()
reader.close()
res(answer)
})
})
}
let questions = [
`What's your name`
,`What's your favorite programming language`
,`What's your favorite async function`
]
let responses = {}
async function getResponses(){
// Notice we have to prepend await before calling the async Array function
// in order for it to function as expected
await questions.asyncForEach(async function(question, index){
let answer = await getUserIn(question)
responses[question] = answer
})
}
async function main(){
await getResponses()
log(responses)
}
main()
// Should prompt user for an answer to each question and then
// log each question and answer as an object to the terminal
Nous pourrions faire de même pour certaines des autres fonctions de tableau comme map ...
async function asyncMap(iteratorFunction){
let newMap = []
let indexer = 0
for(let data of this){
newMap[indexer] = await iteratorFunction(data, indexer, this)
indexer++
}
return newMap
}
Array.prototype.asyncMap = asyncMap
... etc :)
Quelques points à noter:
- Votre iteratorFunction doit être une fonction asynchrone ou une promesse
- Toutes les baies créées auparavant
Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>
ne disposeront pas de cette fonctionnalité
Aujourd'hui, je suis tombé sur plusieurs solutions pour cela. Exécution des fonctions d'attente asynchrone dans la boucle forEach. En construisant le wrapper autour, nous pouvons y arriver.
Les multiples moyens par lesquels cela peut être fait et ils sont les suivants,
Méthode 1: Utilisation du wrapper.
await (()=>{
return new Promise((resolve,reject)=>{
items.forEach(async (item,index)=>{
try{
await someAPICall();
} catch(e) {
console.log(e)
}
count++;
if(index === items.length-1){
resolve('Done')
}
});
});
})();
Méthode 2: Utilisation de la même fonction qu'une fonction générique de Array.prototype
Array.prototype.forEachAsync.js
if(!Array.prototype.forEachAsync) {
Array.prototype.forEachAsync = function (fn){
return new Promise((resolve,reject)=>{
this.forEach(async(item,index,array)=>{
await fn(item,index,array);
if(index === array.length-1){
resolve('done');
}
})
});
};
}
Utilisation:
require('./Array.prototype.forEachAsync');
let count = 0;
let hello = async (items) => {
// Method 1 - Using the Array.prototype.forEach
await items.forEachAsync(async () => {
try{
await someAPICall();
} catch(e) {
console.log(e)
}
count++;
});
console.log("count = " + count);
}
someAPICall = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("done") // or reject('error')
}, 100);
})
}
hello(['', '', '', '']); // hello([]) empty array is also be handled by default
Méthode 3:
Utilisation de Promise.all
await Promise.all(items.map(async (item) => {
await someAPICall();
count++;
}));
console.log("count = " + count);
Méthode 4: boucle for traditionnelle ou boucle for moderne
// Method 4 - using for loop directly
// 1. Using the modern for(.. in..) loop
for(item in items){
await someAPICall();
count++;
}
//2. Using the traditional for loop
for(let i=0;i<items.length;i++){
await someAPICall();
count++;
}
console.log("count = " + count);
Vous pouvez utiliser Array.prototype.forEach
, mais async / await n'est pas si compatible. Cela est dû au fait que la promesse retournée par un rappel asynchrone s'attend à être résolue, mais Array.prototype.forEach
ne résout aucune promesse de l'exécution de son rappel. Ainsi, vous pouvez utiliser forEach, mais vous devrez gérer vous-même la résolution de la promesse.
Voici un moyen de lire et d'imprimer chaque fichier en série en utilisant Array.prototype.forEach
async function printFilesInSeries () {
const files = await getFilePaths()
let promiseChain = Promise.resolve()
files.forEach((file) => {
promiseChain = promiseChain.then(() => {
fs.readFile(file, 'utf8').then((contents) => {
console.log(contents)
})
})
})
await promiseChain
}
Voici un moyen (toujours utilisé Array.prototype.forEach
) d'imprimer le contenu des fichiers en parallèle
async function printFilesInParallel () {
const files = await getFilePaths()
const promises = []
files.forEach((file) => {
promises.push(
fs.readFile(file, 'utf8').then((contents) => {
console.log(contents)
})
)
})
await Promise.all(promises)
}
Pour voir comment cela peut mal tourner, imprimez console.log à la fin de la méthode.
Choses qui peuvent mal tourner en général:
- Ordre arbitraire.
- printFiles peut terminer son exécution avant l'impression des fichiers.
- Mauvaise performance.
Celles-ci ne sont pas toujours erronées mais sont fréquemment utilisées dans des cas d'utilisation standard.
En règle générale, l'utilisation de forEach entraînera tous les fichiers sauf le dernier. Il appellera chaque fonction sans attendre la fonction, ce qui signifie qu'il dit à toutes les fonctions de démarrer puis se termine sans attendre la fin des fonctions.
import fs from 'fs-promise'
async function printFiles () {
const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'))
for(const file of files)
console.log(await file)
}
printFiles()
C'est un exemple en JS natif qui préservera l'ordre, empêchera la fonction de revenir prématurément et en théorie conservera des performances optimales.
Cette volonté:
- Lancez toutes les lectures de fichiers en parallèle.
- Préservez l'ordre via l'utilisation de la carte pour mapper les noms de fichiers aux promesses à attendre.
- Attendez chaque promesse dans l'ordre défini par le tableau.
Avec cette solution, le premier fichier sera affiché dès qu'il sera disponible sans avoir à attendre que les autres soient disponibles au préalable.
Il chargera également tous les fichiers en même temps plutôt que d'avoir à attendre que le premier se termine avant que la deuxième lecture de fichier puisse être lancée.
Le seul inconvénient de ceci et de la version originale est que si plusieurs lectures sont lancées à la fois, il est plus difficile de gérer les erreurs en raison du plus grand nombre d'erreurs pouvant survenir à la fois.
Avec des versions qui lisent un fichier à la fois, alors s'arrêtera en cas d'échec sans perdre de temps à essayer de lire d'autres fichiers. Même avec un système d'annulation élaboré, il peut être difficile d'éviter qu'il échoue sur le premier fichier mais en lisant déjà la plupart des autres fichiers.
Les performances ne sont pas toujours prévisibles. Alors que de nombreux systèmes seront plus rapides avec des lectures de fichiers parallèles, certains préféreront séquentiels. Certains sont dynamiques et peuvent se déplacer sous charge, les optimisations qui offrent une latence ne donnent pas toujours un bon débit en cas de forte contention.
Il n'y a pas non plus de gestion des erreurs dans cet exemple. Si quelque chose exige qu'ils soient tous affichés avec succès ou pas du tout, cela ne le fera pas.
Une expérimentation approfondie est recommandée avec console.log à chaque étape et de fausses solutions de lecture de fichiers (délai aléatoire à la place). Bien que de nombreuses solutions semblent faire la même chose dans des cas simples, toutes présentent des différences subtiles qui nécessitent un examen plus approfondi pour être évitées.
Utilisez cette simulation pour faire la différence entre les solutions:
(async () => {
const start = +new Date();
const mock = () => {
return {
fs: {readFile: file => new Promise((resolve, reject) => {
// Instead of this just make three files and try each timing arrangement.
// IE, all same, [100, 200, 300], [300, 200, 100], [100, 300, 200], etc.
const time = Math.round(100 + Math.random() * 4900);
console.log(`Read of ${file} started at ${new Date() - start} and will take ${time}ms.`)
setTimeout(() => {
// Bonus material here if random reject instead.
console.log(`Read of ${file} finished, resolving promise at ${new Date() - start}.`);
resolve(file);
}, time);
})},
console: {log: file => console.log(`Console Log of ${file} finished at ${new Date() - start}.`)},
getFilePaths: () => ['A', 'B', 'C', 'D', 'E']
};
};
const printFiles = (({fs, console, getFilePaths}) => {
return async function() {
const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'));
for(const file of files)
console.log(await file);
};
})(mock());
console.log(`Running at ${new Date() - start}`);
await printFiles();
console.log(`Finished running at ${new Date() - start}`);
})();
Semblable à celui d'Antonio Val p-iteration
, un module npm alternatif est async-af
:
const AsyncAF = require('async-af');
const fs = require('fs-promise');
function printFiles() {
// since AsyncAF accepts promises or non-promises, there's no need to await here
const files = getFilePaths();
AsyncAF(files).forEach(async file => {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
});
}
printFiles();
Alternativement, async-af
a une méthode statique (log / logAF) qui enregistre les résultats des promesses:
const AsyncAF = require('async-af');
const fs = require('fs-promise');
function printFiles() {
const files = getFilePaths();
AsyncAF(files).forEach(file => {
AsyncAF.log(fs.readFile(file, 'utf8'));
});
}
printFiles();
Cependant, le principal avantage de la bibliothèque est que vous pouvez enchaîner des méthodes asynchrones pour faire quelque chose comme:
const aaf = require('async-af');
const fs = require('fs-promise');
const printFiles = () => aaf(getFilePaths())
.map(file => fs.readFile(file, 'utf8'))
.forEach(file => aaf.log(file));
printFiles();