Résoudre les problèmes de rappel avec Async

Lorsque nous commençons à programmer, nous apprenons qu’un bloc de code s’exécute de haut en bas. Il s'agit d'une programmation synchrone: chaque opération est terminée avant le début de la suivante. C’est génial lorsque vous faites beaucoup de choses qui ne prennent pratiquement pas de temps à un ordinateur, comme l’ajout de nombres, la manipulation d’une chaîne de caractères ou l’affectation de variables. 

Que se passe-t-il lorsque vous voulez faire quelque chose qui prend un temps relativement long, comme accéder à un fichier sur disque, envoyer une requête réseau ou attendre la fin du délai? En programmation synchrone, votre script ne peut rien faire d’autre en attendant. 

Cela peut convenir pour quelque chose de simple ou dans une situation où plusieurs instances de votre script sont en cours d'exécution, mais pour de nombreuses applications serveur, c'est un cauchemar.. 

Entrez la programmation asynchrone. Dans un script asynchrone, votre code continue de s'exécuter en attendant que quelque chose se produise, mais peut revenir en arrière lorsque cela s'est déjà produit.. 

Prenons, par exemple, une requête réseau. Si vous adressez une requête réseau à un serveur lent dont le délai de réponse est de trois secondes, votre script peut effectuer activement d'autres tâches pendant que ce serveur lent répond. Dans ce cas, trois secondes peuvent sembler bien peu à un humain, mais un serveur peut répondre à des milliers de demandes en attendant. Alors, comment gérez-vous l'asynchronisme dans Node.js?? 

La méthode la plus simple consiste à effectuer un rappel. Un rappel est simplement une fonction appelée lorsqu'une opération asynchrone est terminée. Par convention, les fonctions de rappel de Node.js ont au moins un argument, se tromper. Les rappels peuvent avoir plus d’arguments (qui représentent généralement les données renvoyées au rappel), mais le premier sera se tromper. Comme vous l'avez peut-être deviné, se tromper contient un objet d'erreur (si une erreur a été déclenchée, plus à ce sujet plus tard).

Jetons un coup d'oeil à un exemple très simple. Nous utiliserons le module de système de fichiers intégré de Node.js (fs). Dans ce script, nous lirons le contenu d'un fichier texte. La dernière ligne du fichier est un console.log cela pose une question: si vous exécutez ce script, pensez-vous que vous verrez le journal avant de voir le contenu du fichier texte?

var fs = require ('fs'); fs.readFile ('a-text-file.txt', // le nom de fichier d'un fichier texte qui dit "Hello!" 'utf8', // l'encodage du fichier, dans ce cas, la fonction utf-8 (err , text) // le rappel console.log ('Error:', err); // Erreurs, le cas échéant console.log ('Text:', text); // le contenu du fichier); // Sera-ce avant ou après l'erreur / texte? console.log ('Est-ce que cela est enregistré avant ou après le contenu du fichier texte?'); 

Comme cela est asynchrone, nous verrons en fait le dernier console.log avant le contenu du fichier texte. Si vous avez un fichier nommé a-text-file.txt dans le même répertoire dans lequel vous exécutez votre script de noeud, vous verrez que se tromper est nul, et la valeur de texte est rempli avec le contenu du fichier texte. 

Si vous n'avez pas de fichier nommé a-text-file.txt, se tromper retournera un objet Error, et la valeur de texte sera indéfini. Cela conduit à un aspect important des rappels: vous devez toujours gérer vos erreurs. Pour gérer les erreurs, vous devez vérifier une valeur dans le se trompervariable; si une valeur est présente, une erreur est survenue. Par convention, se tromper les arguments ne reviennent généralement pas faux, de sorte que vous ne pouvez vérifier que la vérité.

var fs = require ('fs'); fs.readFile ('a-text-file.txt', // le nom de fichier d'un fichier texte qui dit "Hello!" 'utf8', // l'encodage du fichier, dans ce cas, la fonction utf-8 (err , text) // le callback if (err) console.error (err); // affiche une erreur sur la console else console.log ('Text:', text); // pas d'erreur, donc affichez le contenu du fichier);

Supposons maintenant que vous souhaitiez afficher le contenu de deux fichiers dans un ordre particulier. Vous allez vous retrouver avec quelque chose comme ça:

var fs = require ('fs'); fs.readFile ('a-text-file.txt', // le nom de fichier d'un fichier texte qui dit "Hello!" 'utf8', // l'encodage du fichier, dans ce cas, la fonction utf-8 (err , text) // le callback if (err) console.error (err); // affiche une erreur sur la console else console.log ('Premier fichier texte:', text); // pas d'erreur, afin d'afficher le contenu du fichier fs.readFile ('another-text-file.txt', // le nom du fichier d'un fichier texte qui dit "Hello!" 'utf8', // l'encodage du fichier, dans ce cas , utf-8 function (err, text) // le rappel si (err) console.error (err); // affiche une erreur sur la console else console.log ('Deuxième fichier texte:', texte ); // pas d'erreur, affichez le contenu du fichier););

Le code semble assez méchant et a un certain nombre de problèmes:

  1. Vous chargez les fichiers séquentiellement; il serait plus efficace de les charger tous les deux en même temps et de renvoyer les valeurs lorsque les deux sont complètement chargés.

  2. Syntaxiquement, c'est correct mais difficile à lire. Notez le nombre de fonctions imbriquées et les onglets croissants. Vous pouvez faire quelques astuces pour améliorer l'apparence, mais vous sacrifiez peut-être la lisibilité par d'autres moyens..

  3. Ce n'est pas un but très général. Cela fonctionne bien pour deux fichiers, mais que se passe-t-il si vous avez parfois neuf fichiers et 22 fois ou un seul? La façon dont il est écrit est très rigide.

Ne vous inquiétez pas, nous pouvons résoudre tous ces problèmes (et plus) avec async.js.

Rappels avec Async.js

Commençons par installer le module async.js..

npm install async --save

Async.js peut être utilisé pour coller des tableaux de fonctions en série ou en parallèle. Réécrivons notre exemple:

var async = require ('async'), //async.js module fs = require ('fs'); async.series (// exécute les fonctions du premier argument les unes après les autres) [// Le premier argument est un tableau de fonctions function (cb) // 'cb' est un raccourci pour "callback" fs.readFile ('a- text-file.txt ',' utf8 ', cb);, fonction (cb) fs.readFile (' another-text-file.txt ',' utf8 ', cb);], fonction (err, valeurs ) // Le rappel "done" qui est exécuté une fois les fonctions du tableau terminées si (err) // Si des erreurs se sont produites lors de l'exécution des fonctions du tableau, elles seront envoyées sous la forme suivante: console.error ( err); else // Si err est faux, alors tout va bien console.log ('Premier fichier texte:', valeurs [0]); console.log ('Deuxième fichier texte:', valeurs [1]); );

Cela fonctionne presque comme dans l'exemple précédent, en chargeant séquentiellement chaque fichier et ne diffère que par le fait qu'il lit chaque fichier et n'affiche pas le résultat tant qu'il n'est pas complet. Le code est plus concis et plus propre que l'exemple précédent (et nous le rendrons encore meilleur par la suite). async.series prend un tableau de fonctions et les exécute les unes après les autres. 

Chaque fonction ne doit avoir qu’un seul argument, le rappel (ou cb dans notre code). cbdevrait être exécuté avec le même type d’arguments que n’importe quel autre rappel, afin que nous puissions le mettre dans notre droit fs.readFile arguments. 

Enfin, les résultats sont envoyés au dernier rappel, le deuxième argument pour async.series. Les résultats sont stockés dans un tableau avec les valeurs en corrélation avec l’ordre des fonctions dans le premier argument de async.series.

Avec async.js, la gestion des erreurs est simplifiée car si elle rencontre une erreur, elle renvoie l'erreur à l'argument du rappel final et n'exécutera aucune autre fonction asynchrone.. 

Tous ensemble maintenant

Une fonction associée est async.parallel; il a les mêmes arguments que async.series afin que vous puissiez changer entre les deux sans modifier le reste de votre syntaxe. C’est un bon point pour couvrir le parallèle par rapport au concurrent. 

JavaScript est fondamentalement un langage à un seul thread, ce qui signifie qu'il ne peut faire qu'une chose à la fois. Il est capable d'effectuer certaines tâches dans un thread séparé (la plupart des fonctions d'E / S, par exemple), et c'est ici que la programmation asynchrone entre en jeu avec JS. Ne confondez pas parallèle avec concurrence

Lorsque vous exécutez deux choses avec async.parallel, vous ne le faites pas ouvrir un autre thread pour analyser JavaScript ou faire deux choses à la fois - vous contrôlez vraiment quand il passe entre les fonctions dans le premier argument de async.parallel. Donc, vous ne gagnez rien en mettant simplement du code synchrone dans async.parallel. 

Ceci s’explique mieux visuellement:

Voici notre exemple précédent écrit pour être parallèle, la seule différence est que nous utilisons async.parallel plutôt que async.series.

var async = require ('async'), //async.js module fs = require ('fs'); async.parallel (// exécute les fonctions dans le premier argument, mais n'attendez pas que la première fonction finisse pour démarrer le deuxième [// Le premier argument est un tableau de fonctions function (cb) // 'cb' est un raccourci pour "callback" fs.readFile ('a-text-file.txt', 'utf8', cb);, fonction (cb) fs.readFile ('another-text-file.txt', 'utf8 ', cb);], function (err, values) // Le rappel "done" exécuté une fois les fonctions du tableau terminées if (err) // Si des erreurs se sont produites lors de l'exécution des fonctions du tableau , ils seront envoyés en tant que err console.error (err); else // Si err est faux, alors tout va bien console.log ('Premier fichier texte:', valeurs [0]); console.log ( 'Deuxième fichier texte:', valeurs [1]););

Encore et encore

Nos exemples précédents ont exécuté un nombre fixe d'opérations, mais que se passe-t-il si vous avez besoin d'un nombre variable d'opérations asynchrones? Cela devient vite compliqué si vous vous fiez uniquement aux callbacks et à la construction de langage habituelle, aux compteurs maladroits ou aux vérifications de condition qui masquent le sens réel de votre code. Jetons un coup d'œil à l'équivalent approximatif d'une boucle for avec async.js.

Dans cet exemple, nous allons écrire dix fichiers dans le répertoire en cours avec des noms de fichiers séquentiels et un contenu court. Vous pouvez faire varier le nombre en modifiant la valeur du premier argument de async.times. Dans cet exemple, le rappel pour fs.writeFile crée seulement un se tromper argument, mais le async.times fonction peut également prendre en charge une valeur de retour. Comme async.series, il est transmis au rappel effectué dans le deuxième argument sous forme de tableau..

var async = require ('async'), fs = require ('fs'); async.times (10, // nombre de fois pour exécuter la fonction function (runCount, callback) fs.writeFile ('fichier -' + runCount + '. txt', // le nouveau nom de fichier 'Ceci est le numéro de fichier' + runCount, // le contenu du nouveau rappel de fichier);, function (err) if (err) console.error (err); else console.log ('Fichiers écrits.'););

C'est un bon moment pour dire que la plupart des fonctions async.js, par défaut, fonctionnent en parallèle plutôt qu'en série. Donc, dans l'exemple ci-dessus, il va commencer à créer les fichiers et à signaler quand tous sont complètement créés et écrits. 

Les fonctions qui sont exécutées en parallèle par défaut ont une fonction de série corollaire indiquée par la fonction se terminant par, vous l'avez deviné, 'Série'. Donc, si vous voulez exécuter cet exemple en série plutôt qu'en parallèle, vous devriez changer async.times à async.timesSeries.

Pour notre prochain exemple de mise en boucle, examinons la fonction async.until. async.until exécute une fonction asynchrone (en série) jusqu'à ce qu'une condition particulière soit remplie. Cette fonction prend trois fonctions en argument. 

La première fonction est le test dans lequel vous renvoyez true (si vous voulez arrêter la boucle) ou false (si vous souhaitez continuer la boucle). Le deuxième argument est la fonction asynchrone et le dernier est le rappel effectué. Jetez un oeil à cet exemple:

var async = require ('async'), fs = require ('fs'), startTime = new Date (). getTime (), // l'horodatage unix en millisecondes runCount = 0; async.until (function () // renvoie true si 4 millisecondes se sont écoulées, sinon false (et continue à exécuter le script) renvoie new Date (). getTime ()> (startTime + 5);, function (rappel)  runCount + = 1; fs.writeFile ('timed-file -' + runCount + '. txt', // le nouveau nom de fichier 'Ceci est le numéro de fichier' + runCount, // le contenu du nouveau rappel de fichier);, function (err) if (err) console.error (err); else console.log ('Fichiers écrits.'););

Ce script créera de nouveaux fichiers texte pendant cinq millisecondes. Au début du script, nous obtenons l'heure de début à l'époque milliseconde unix, puis dans la fonction test, nous obtenons l'heure actuelle et testons si elle est supérieure de cinq millisecondes à l'heure de début plus cinq. Si vous exécutez ce script plusieurs fois, vous obtiendrez peut-être des résultats différents.. 

Sur ma machine, je créais entre 6 et 20 fichiers en cinq millisecondes. Fait intéressant, si vous essayez d’ajouter console.log que ce soit dans la fonction test ou dans la fonction asynchrone, vous obtiendrez des résultats très différents car l’écriture sur votre console prend du temps. Cela montre simplement que dans le logiciel, tout a un coût de performance!

La boucle for each est une structure pratique qui vous permet de faire quelque chose pour chaque élément d'un tableau. Dans async.js, ce serait le async.each une fonction. Cette fonction prend trois arguments: la collection ou le tableau, la fonction asynchrone à exécuter pour chaque élément et le rappel effectué.. 

Dans l'exemple ci-dessous, nous prenons un tableau de chaînes (dans ce cas, des types de races de chiens de lévriers) et créons un fichier pour chaque chaîne. Lorsque tous les fichiers ont été créés, le rappel effectué est exécuté. Comme on pouvait s’y attendre, les erreurs sont gérées via le se tromper objet dans le rappel fait. async.each est exécuté en parallèle, mais si vous souhaitez l'exécuter en série, vous pouvez suivre le modèle mentionné précédemment et utiliser async.eachSeries au lieu de async.each.

var async = require ('async'), fs = require ('fs'); async.each (// un éventail de races de chiens de lévriers ['lévriers', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-lévrier'], fonction ( dogBreed, rappel) fs.writeFile (dogBreed + '. txt', // le nouveau nom du fichier 'fichier pour les chiens de la race' + dogBreed, // le contenu du nouveau fichier callback);, function (err)  if (err) console.error (err); else console.log ('Écrit des fichiers sur des chiens.');));

Un cousin de async.each est le async.map une fonction; la différence est que vous pouvez renvoyer les valeurs à votre rappel effectué. Avec le async.map fonction, vous transmettez un tableau ou une collection en tant que premier argument, puis une fonction asynchrone est exécutée sur chaque élément du tableau ou de la collection. Le dernier argument est le rappel effectué. 

L'exemple ci-dessous prend le tableau des races de chiens et utilise chaque élément pour créer un nom de fichier. Le nom du fichier est ensuite transmis à fs.readFile, où il est lu et les valeurs sont renvoyées par la fonction de rappel. Vous vous retrouvez avec un tableau du contenu du fichier dans les arguments de rappel terminés.

var async = require ('async'), fs = require ('fs'); async.map (['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italien-lévrier'], fonction (dogBreed, callback) fs.readFile (dogBreed + '. txt', // le nouveau nom de fichier 'utf8', rappel);, function (err, dogBreedFileContents) if (err) console.error (err); else console.log ('dog races '); console.log (dogBreedFileContents););

async.filter est également très similaire dans la syntaxe à async.each et async.map, mais avec le filtre, vous envoyez une valeur booléenne au rappel d’élément plutôt qu’à la valeur du fichier. Dans le callback done, vous obtenez un nouveau tableau, avec seulement les éléments que vous avez passés à un vrai ou valeur de vérité pour dans le rappel d'article. 

var async = require ('async'), fs = require ('fs'); async.filter (['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italien-lévrier'], fonction (dogBreed, callback) fs.readFile (dogBreed + '. txt', // le nouveau nom de fichier 'utf8', function (err, fileContents) if (err) callback (err); else callback (err, // ce sera faux depuis que nous avons vérifié ci-dessus fileContents.match (/ greyhound / gi) // utilisez RegExp pour rechercher la chaîne 'greyhound' dans le contenu du fichier););, function (err, dogBreedFileContents) if (err) console .error (err); else console.log ('races de lévriers:'); console.log (dogBreedFileContents););

Dans cet exemple, nous faisons un peu plus de choses que dans les exemples précédents. Remarquez comment nous ajoutons un appel de fonction supplémentaire et gérons notre propre erreur. le si se tromper et rappel (err) Le motif est très utile si vous devez manipuler les résultats d'une fonction asynchrone, mais vous voulez toujours laisser async.js gérer les erreurs.. 

De plus, vous remarquerez que nous utilisons la variable err comme premier argument de la fonction de rappel. À première vue, cela ne semble pas très correct. Mais comme nous avons déjà vérifié la véracité de err, nous savons qu'il est faux et sûr de passer au rappel.. 

Au bord d'une falaise

Jusqu'à présent, nous avons exploré un certain nombre de blocs de construction utiles dont les corollaires sont approximatifs dans la programmation synchrone. Plongeons droit dans async.waterfall, qui n'a pas beaucoup d'équivalent dans le monde synchrone. 

Le concept avec une cascade est que les résultats d'une fonction asynchrone se répercutent dans les arguments d'une autre fonction asynchrone en série. C'est un concept très puissant, en particulier lorsque vous essayez d'enchaîner plusieurs fonctions asynchrones qui s'appuient les unes sur les autres. Avec async.waterfall, le premier argument est un tableau de fonctions, et le second argument est votre rappel effectué. 

Dans votre tableau de fonctions, la première fonction commencera toujours par un seul argument, le rappel. Chaque fonction suivante doit correspondre aux arguments non-err de la fonction précédente sans la fonction err et avec l'ajout du nouveau rappel.

Dans notre prochain exemple, nous allons commencer à combiner certains concepts en utilisant une cascade comme la colle. Dans le tableau qui constitue le premier argument, nous avons trois fonctions: la première charge la liste de répertoires à partir du répertoire actuel, la seconde prend la liste de répertoires et utilise async.map courir fs.stat sur chaque fichier, et la troisième fonction prend la liste de répertoires du résultat de la première fonction et obtient le contenu de chaque fichier (fs.readFile).

async.waterfall exécute chaque fonction séquentiellement, de sorte qu’il exécute toujours tous les fs.stat fonctions avant d'exécuter fs.readFile. Dans ce premier exemple, les deuxième et troisième fonctions ne sont pas dépendantes l'une de l'autre et peuvent donc être encapsulées dans une async.parallel pour réduire le temps total d'exécution, mais nous modifierons à nouveau cette structure pour l'exemple suivant.

Remarque: Exécutez cet exemple dans un petit répertoire de fichiers texte, sinon vous risquez de perdre beaucoup de mémoire pendant une longue période..

var async = require ('async'), fs = require ('fs'); async.waterfall ([fonction (rappel) fs.readdir ('.', rappel)); // lit le répertoire en cours, le transmet à la fonction suivante., fonction (nomsFichiers, rappel) // 'nomsFichiers' est la liste de répertoires de la fonction précédente async.map (nomfichiers, // La liste de répertoires est juste un tableau de noms de fichiers, fs.stat, // afin que nous puissions utiliser async.map pour exécuter fs.stat pour chaque fonction de nom de fichier (err , stats) if (err) callback (); else callback (err, FileNames, stats); // transmet le message d'erreur, la liste des répertoires et la collection de statistiques à l'élément suivant de la cascade) ;, fonction (nomsFichiers, statistiques, rappel) // la liste de répertoires, 'nomFichiers' est jointe à la collection d'objets fs.stat dans 'stats' async.map (nomsFichiers, fonction (aFileName, readCallback) // Cette fois, nous prenons les noms de fichiers avec map et les transmettons à fs.readFile pour obtenir le contenu de fs.readFile (aFileName, 'utf8', readCallback);, fonction (err, contenu) if (err) callback (err); else // Maintenant notre rappel w ill a trois arguments, la liste de répertoire originale ('fileNames'), la collection fs.stats et un tableau contenant le contenu de chaque rappel de fichier (err, nomFichier, statistique, contenu); ); ], fonction (err, noms de fichier, statistiques, contenu) if (err) console.error (err);  else console.log (noms_fichiers); console.log (stats); console.log (contenu); );

Disons que nous voulons obtenir les résultats des fichiers dont la taille est supérieure à 500 octets. Nous pourrions utiliser le code ci-dessus, mais vous obtiendriez la taille et le contenu de chaque fichier, que vous en ayez besoin ou non. Comment pouvez-vous simplement obtenir la stat des fichiers et uniquement le contenu des fichiers qui atteignent la taille requise? 

Premièrement, nous pouvons extraire toutes les fonctions anonymes en fonctions nommées. C'est une préférence personnelle, mais cela rend le code un peu plus propre et plus facile à comprendre (réutilisable pour démarrer). Comme vous pouvez l’imaginer, vous devez connaître les tailles, les évaluer et obtenir uniquement le contenu des fichiers qui dépasse la taille requise. Ceci peut être facilement accompli avec quelque chose comme Array.filter, mais c'est une fonction synchrone et async.waterfall attend des fonctions de style asynchrone. Async.js dispose d’une fonction d’aide qui permet d’intégrer des fonctions synchrones dans des fonctions asynchrones, le nom plutôt async.asyncify.

Nous devons faire trois choses, toutes avec lesquelles nous allons terminer async.asyncify. Tout d’abord, prenons les tableaux de noms de fichiers et de statistiques du tableauFsStat fonction, et nous les fusionnerons en utilisant carte. Ensuite, nous filtrerons tous les éléments dont la taille de statistique est inférieure à 300. Enfin, prenons le nom de fichier combiné et l’objet stat et utilisons carte encore une fois pour obtenir le nom de fichier. 

Après avoir obtenu les noms des fichiers dont la taille est inférieure à 300, nous utiliserons async.map et fs.readFile pour obtenir le contenu. Il existe de nombreuses façons de craquer cet œuf, mais dans notre cas, il a été fractionné pour offrir une flexibilité maximale et une réutilisation du code. Ce async.waterfall utilisation illustre comment vous pouvez mélanger et faire correspondre le code synchrone et asynchrone.

var async = require ('async'), fs = require ('fs'); // Notre anonyme refactored dans les fonctions nommées function directory directory (callback) fs.readdir ('.', Callback);  function arrayFsStat (noms_fichiers, rappel) async.map (noms_fichiers, fs.stat, fonction (err, stats) if (erreur) callback (err); else rappel (err, nomsFichier, stats); )  function tableauFsReadFile (nomsFichiers, rappel) async.map (nomsFichiers, fonction (aNomFichier, readCallback) fs.readFile (nomFichier, 'utf8', readCallback);, fonction (err, contenu) if (err) callback (err); else callback (err, contenu););  // Ces fonctions sont des fonctions synchrones mergeFilenameAndStat (nomsfichiers, stats) return stats.map (fonction (aStatObj, index) aStatObj.fileName = nomsFichiers [index]; renvoyé aStatObj;);  function above300 (CombinedFilenamesAndStats) return: CombinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;);  function justFilenames (CombinedFilenamesAndStats) return CombinedNomsFilenAndStats .map (function (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;);  async.waterfall ([directoryListing, arrayFsStat, async.asyncify (mergeFilenameAndStat), // asyncify encapsule les fonctions synchrones dans un rappel err-first async.asyncify (au-dessus de 300), async.asyncify (justFilenames), arrayFsReadFile], function (err, contenu) if (err) console.error (err); else console.log (contenu););

Allant plus loin, affinons encore plus notre fonction. Disons que nous voulons écrire une fonction qui fonctionne exactement comme ci-dessus, mais avec la possibilité de regarder dans n'importe quel chemin. Un proche cousin de async.waterfall est async.seq. Tandis que async.waterfall exécute juste une cascade de fonctions, async.seq renvoie une fonction qui exécute une cascade d'autres fonctions. En plus de créer une fonction, vous pouvez transmettre des valeurs qui iront dans la première fonction asynchrone. 

Conversion en async.seq ne prend que quelques modifications. Tout d'abord, nous allons modifier liste du répertoire accepter un argument, ce sera le chemin. Deuxièmement, nous allons ajouter une variable pour tenir notre nouvelle fonction (répertoireAbove300). Troisièmement, nous allons prendre l'argument tableau de la async.waterfall et traduire cela en arguments pour async.seq. Notre rappel terminé pour la cascade est maintenant utilisé comme rappel lorsque nous courons répertoireAbove300.

var async = require ('async'), fs = require ('fs'), directoryAbove300; function directoryListing (initialPath, callback) // nous pouvons passer une variable à la première fonction utilisée dans async.seq - la fonction résultante peut accepter des arguments et leur transmettre cette première fonction fs.readdir (initialPath, callback);  function arrayFsStat (noms_fichiers, rappel) async.map (noms_fichiers, fs.stat, fonction (err, stats) if (erreur) callback (err); else rappel (err, nomsFichier, stats); )  function tableauFsReadFile (nomsFichiers, rappel) async.map (nomsFichiers, fonction (aNomFichier, readCallback) fs.readFile (nomFichier, 'utf8', readCallback);, fonction (err, contenu) if (err) callback (err); else callback (err, contenu););  function mergeFilenameAndStat (nomsfichiers, stats) return stats.map (fonction (aStatObj, index) aStatObj.fileName = nomFichiers [index]; renvoyer aStatObj;);  function above300 (CombinedFilenamesAndStats) return: CombinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;);  function justFilenames (CombinedFilenamesAndStats) return CombinedNomsAndStats .map (function (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;) //async.seq produira une nouvelle fonction arrayFsStat, async.asyncify (mergeFilenameAndStat), async.asyncify (au-dessus de 300), async.asyncify (noms de fichiers seulement), arrayFsReadFile); directoryAbove300 ('.', fonction (err, noms de fichier, statistiques, contenu) if (err) console.error (err); else console.log (noms de fichier));

Note sur les promesses et les fonctions asynchrones

Vous vous demandez peut-être pourquoi je n'ai pas mentionné les promesses. Je n'ai rien contre eux - ils sont assez pratiques et peut-être une solution plus élégante que les rappels - mais ils sont une façon différente de regarder le codage asynchrone. 

Utilisation intégrée des modules Node.js se tromper-premiers rappels, et des milliers d'autres modules utilisent ce modèle. En fait, c’est pourquoi ce tutoriel utilise fs dans les exemples - quelque chose d'aussi fondamental que l'accès au système de fichiers dans Node.js utilise des rappels;.  

Il est possible d'utiliser quelque chose comme Bluebird pour incorporer des rappels err-first dans des fonctions basées sur Promise, mais cela ne vous amène qu'à ce jour-Async.js fournit une multitude de métaphores qui rendent le code asynchrone lisible et gérable..

Embrace Asynchrony

JavaScript est devenu l'un des langages de facto du travail sur le Web. Ce n’est pas sans ses courbes d’apprentissage, et il existe de nombreux cadres et bibliothèques pour vous tenir occupé. Si vous recherchez des ressources supplémentaires à étudier ou à utiliser dans votre travail, consultez ce que nous avons à votre disposition sur le marché Envato..

Mais apprendre asynchrone est quelque chose de complètement différent, et ce tutoriel vous a montré à quel point il peut être utile.

L'asynchronisme est essentiel pour l'écriture de code JavaScript côté serveur. Cependant, s'il n'est pas conçu correctement, votre code peut devenir une bête de rappels ingérable. En utilisant une bibliothèque telle que async.js qui fournit un certain nombre de métaphores, vous pouvez trouver qu’écrire du code asynchrone est une joie..