Tutorial

Comment écrire un code asynchrone dans Node.js

Node.jsDevelopmentJavaScript

L'auteur a choisi le Open Internet/Free Speech Fund pour recevoir un don dans le cadre du programme Write for DOnations.

Introduction

Pour de nombreux programmes en JavaScript, le code est exécuté comme le développeur l'écrit, ligne par ligne. Cela est appelé exécution synchrone, car les lignes sont exécutées l'une après l'autre, dans l'ordre où elles ont été écrites. Cependant, toutes les instructions que vous donnez à l'ordinateur ne doivent pas être suivies immédiatement. Par exemple, si vous envoyez une requête réseau, le processus d'exécution de votre code devra attendre que les données soient retournées avant qu'il ne puisse travailler dessus. Dans ce cas, ce serait une perte de temps s'il n'exécutait pas d'autre code en attendant que la requête réseau soit terminée. Pour résoudre ce problème, les développeurs utilisent la programmation asynchrone, dans laquelle les lignes de code sont exécutées dans un ordre différent de celui dans lequel elles ont été écrites. Avec la programmation asynchrone, nous pouvons exécuter d'autres codes en attendant la fin de longues activités comme les requêtes réseau.

Le code JavaScript est exécuté sur un seul thread dans un processus informatique. Son code est traité de manière synchrone sur ce thread, avec une seule instruction exécutée à la fois. Par conséquent, si nous devons effectuer une tâche longue sur ce thread, le code restant est bloqué jusqu'à ce que la tâche soit terminée. En exploitant les fonctionnalités de programmation asynchrone de JavaScript, nous pouvons décharger les tâches de longue durée sur un thread en arrière-plan pour éviter ce problème. Lorsque la tâche est terminée, le code dont nous avons besoin pour traiter les données de la tâche est remis sur l'unique thread principal.

Dans ce tutoriel, vous apprendrez comment JavaScript gère les tâches asynchrones avec l'aide de la boucle événementielle (construction JavaScript qui permet de terminer une nouvelle tâche tout en attendant une autre). Vous allez ensuite créer un programme qui utilise une programmation asynchrone pour demander une liste de films à une API Studio Ghibli et enregistrer les données dans un fichier CSV. Le code asynchrone sera écrit de trois façons : les callbacks, les promesses et les mots-clés async/await.

Remarque : Actuellement, la programmation asynchrone ne se fait plus uniquement à l'aide de callbacks, mais l'apprentissage de cette méthode obsolète peut fournir un excellent contexte pour expliquer pourquoi la communauté JavaScript utilise maintenant des promesses. Les mots-clés async/await nous permettent d'utiliser les promesses de manière moins prolixe, et représentent donc la manière standard de faire de la programmation asynchrone en JavaScript au moment de la rédaction de cet article.

Conditions préalables

La boucle événementielle

Commençons par étudier le fonctionnement interne de l'exécution de la fonction JavaScript. Comprendre comment il se comporte vous permettra d'écrire du code asynchrone plus facilement et vous aidera à résoudre les problèmes de code à l'avenir.

Pendant que l'interpréteur JavaScript exécute le code, chaque fonction appelée est ajoutée à la pile d'appel de JavaScript. La pile d'appel est une pile (une structure de données de type liste dans laquelle les éléments ne peuvent être ajoutés qu'au sommet, et retirés du sommet). Les piles suivent le principe “Dernier entré, premier sorti ” ou principe LIFO. Si vous ajoutez deux éléments à la pile, l'article le plus récemment ajouté est retiré en premier.

Illustrons cela avec un exemple qui utilise la pile d'appel. Si JavaScript rencontre une fonction functionA() qui est appelée, elle est ajoutée à la pile d'appel. Si cette fonction function A() appelle une autre fonction function B(), la fonction functionB() est ajoutée au sommet de la pile d'appel. Quand JavaScript termine l'exécution d'une fonction, elle est retirée de la pile d'appel. Par conséquent, JavaScript exécutera d'abord functionB(), la supprimera de la pile lorsqu'elle sera terminée, puis terminera l'exécution de functionA() et la supprimera de la pile d'appel. C'est pourquoi les fonctions intérieures sont toujours exécutées avant leurs fonctions extérieures.

Lorsque JavaScript rencontre une opération asynchrone, comme l'écriture dans un fichier, elle l'ajoute à une table dans sa mémoire. Cette table stocke l'opération, la condition pour qu'elle soit exécutée et la fonction à appeler lorsqu'elle est terminée. À la fin de l'opération, JavaScript ajoute la fonction associée à la file d'attente des messages. Une file d'attente est une autre structure de données de type liste où les éléments ne peuvent être ajoutés qu'au bas, mais retirés du haut. Dans la file d'attente des messages, si deux ou plusieurs opérations asynchrones sont prêtes à être exécutées, l'opération asynchrone qui a été achevée en premier verra sa fonction marquée pour être exécutée en premier.

Les fonctions dans la file d'attente des messages attendent d'être ajoutées à la pile des appels. La boucle événementielle est un processus perpétuel qui vérifie si la pile d'appel est vide. Si c'est le cas, alors le premier élément de la file d'attente des messages est déplacé vers la pile d'appel. JavaScript donne la priorité aux fonctions de la file d'attente des messages par rapport aux appels de fonctions qu'il interprète dans le code. L'effet combiné de la pile d'appels, de la file d'attente des messages et de la boucle événementielle permet de traiter le code JavaScript tout en gérant les activités asynchrones.

Maintenant que vous avez une bonne compréhension de la boucle d'événements, vous savez comment le code asynchrone que vous écrivez sera exécuté. Grâce à ces connaissances, vous pouvez maintenant créer du code asynchrone avec trois approches différentes : les callbacks, les promesses et async/await.

La programmation asynchrone avec les Callbacks

Une fonction de callback est une fonction qui est transmise comme argument à une autre fonction, puis exécutée lorsque l'autre fonction est terminée. Nous utilisons les callbacks pour nous assurer que le code est exécuté uniquement après la fin d'une opération asynchrone.

Pendant longtemps, les callbacks étaient le mécanisme le plus commun pour écrire du code asynchrone, mais maintenant ils sont devenus largement obsolètes car ils peuvent rendre confuse la lecture du code. Dans cette étape, vous allez écrire un exemple de code asynchrone utilisant les callbacks afin de pouvoir l'utiliser comme référence pour constater le gain d'efficacité des autres stratégies.

Il existe de nombreuses façons d'utiliser les fonctions de callback dans une autre fonction. En général, ils ont cette structure :

function asynchronousFunction([ Function Arguments ], [ Callback Function ]) {
    [ Action ]
}

Bien que la syntaxe de JavaScript ou de Node.js n'exige pas que la fonction de rappel soit le dernier argument de la fonction externe, il s'agit d'une pratique courante qui rend les rappels plus faciles à identifier. Il est également courant, pour les développeurs de JavaScript, d'utiliser une fonction anonyme comme callback. Les fonctions anonymes sont celles créées sans nom. C'est généralement beaucoup plus lisible lorsqu'une fonction est définie à la fin de la liste des arguments.

Pour démontrer les callbacks, nous allons créer un module Node.js qui écrit une liste de films de Studio Ghibli dans un fichier. Tout d'abord, créez un dossier qui va stocker notre fichier JavaScript et sa sortie :

  • mkdir ghibliMovies

Entrez ensuite ce dossier :

  • cd ghibliMovies

Nous allons commencer par effectuer une requête HTTP à l’API de Studio Ghibli, dont notre fonction de rappel enregistrera les résultats. Pour ce faire, nous allons installer une librairie qui nous permet d'accéder aux données d'une réponse HTTP dans un callback.

Dans votre terminal, initialisez npm afin de pouvoir disposer plus tard d'une référence pour nos packages :

  • npm init -y

Ensuite, installez la librairie request :

  • npm i request --save

Ouvrez maintenant un nouveau fichier appelé callbackMovies.js dans un éditeur de texte comme nano :

  • nano callbackMovies.js

Dans votre éditeur de texte, entrez le code suivant. Commençons par envoyer une requête HTTP avec le module de request :

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films');

Dans la première ligne, nous chargeons le module de request qui a été installé via npm. Le module renvoie une fonction qui peut effectuer des requêtes HTTP ; nous enregistrons ensuite cette fonction dans la constante de request.

Nous faisons ensuite la requête HTTP en utilisant la fonction request(). Envoyons maintenant les données de la requête HTTP à la console en ajoutant les changements mis en évidence :

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    movies.forEach(movie => {
        console.log(`${movie['title']}, ${movie['release_date']}`);
    });
});

Lorsque nous utilisons la fonction request(), nous lui donnons deux paramètres :

  • L'URL du site web pour lequel nous essayons de lancer une requête
  • Une fonction callback qui gère toute erreur ou toute réponse réussie après la fin de la requête

Notre fonction de callback a trois arguments : error, response et body. Lorsque la requête HTTP est terminée, les arguments se voient automatiquement attribuer des valeurs en fonction du résultat. Si la requête n'a pas été envoyée, alors error contient un objet, mais response et body sont null. Si la requête a été effectuée avec succès, la réponse HTTP est stockée dans response. Si notre réponse HTTP renvoie des données (dans cet exemple, nous avons JSON), alors les données sont définies dans body.

Notre fonction callback vérifie en premier si nous avons reçu une erreur. La meilleure pratique consiste à vérifier d'abord s'il y a des erreurs dans un callback afin que l'exécution du callback ne se poursuive pas avec des données manquantes. Dans ce cas, nous enregistrons l'erreur et l'exécution de la fonction. Nous vérifions ensuite le code de statut de la réponse. Notre serveur peut ne pas être toujours disponible, et les API peuvent changer, de sorte que des requêtes autrefois sensibles deviennent incorrectes. En vérifiant que le code de statut est 200, ce qui signifie que la requête était “OK”, nous pouvons être sûrs que notre réponse correspond à ce que nous attendons.

Enfin, nous analysons le corps de la réponse dans un Tableau et passons en boucle chaque film pour enregistrer son nom et son année de sortie.

Après avoir enregistré et quitté le fichier, exécutez ce script avec :

  • node callbackMovies.js

Vous aurez le résultat suivant :

Output
Castle in the Sky, 1986 Grave of the Fireflies, 1988 My Neighbor Totoro, 1988 Kiki's Delivery Service, 1989 Only Yesterday, 1991 Porco Rosso, 1992 Pom Poko, 1994 Whisper of the Heart, 1995 Princess Mononoke, 1997 My Neighbors the Yamadas, 1999 Spirited Away, 2001 The Cat Returns, 2002 Howl's Moving Castle, 2004 Tales from Earthsea, 2006 Ponyo, 2008 Arrietty, 2010 From Up on Poppy Hill, 2011 The Wind Rises, 2013 The Tale of the Princess Kaguya, 2013 When Marnie Was There, 2014

Nous avons reçu avec succès une liste des films du Studio Ghibli avec l'année de leur sortie. Terminons maintenant ce programme en écrivant dans un fichier la liste des films que nous sommes en train d'enregistrer.

Mettez à jour le fichier callbackMovies.js dans votre éditeur de texte pour inclure le code mis en évidence suivant, ce qui crée un fichier CSV avec nos données de film :

callbackMovies.js
const request = require('request');
const fs = require('fs');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    let movieList = '';
    movies.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });

    fs.writeFile('callbackMovies.csv', movieList, (error) => {
        if (error) {
            console.error(`Could not save the Ghibli movies to a file: ${error}`);
            return;
        }

        console.log('Saved our list of movies to callbackMovies.csv');;
    });
});

En observant les changements mis en évidence, nous voyons que nous importons le module fs. Ce module est standard dans toutes les installations de Node.js, et il contient une méthode writeFile() qui peut être écrite de manière asynchrone dans un fichier.

Au lieu d'enregistrer les données dans la console, nous les ajoutons maintenant à une variable de chaîne movieList. Nous utilisons ensuite writeFile() pour enregistrer le contenu de movieList dans un nouveau fichier — callbackMovies.csv. Enfin, nous attribuons un callback à la fonction writeFile() qui a un seul argument : error. Cela nous permet de traiter les cas où nous ne sommes pas en mesure d'écrire dans un fichier, par exemple lorsque l'utilisateur sur lequel nous exécutons le processus node n'a pas ces autorisations.

Enregistrez le fichier et exécutez à nouveau ce programme Node.js avec :

  • node callbackMovies.js

Dans votre dossier ghibliMovies, vous verrez callbackMovies.csv, qui a le contenu suivant :

callbackMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Il est important de noter que nous écrivons dans notre fichier CSV lors du callback de la requête HTTP. Lorsque le code est dans la fonction callback, il n'écrira dans le fichier qu'une fois la requête HTTP achevée. Si nous voulions communiquer avec une base de données après avoir écrit notre fichier CSV, nous créerions une autre fonction asynchrone qui serait appelée dans le callback de writeFile(). Plus nous avons de code asynchrone, plus les fonctions de rappel doivent être imbriquées.

Imaginons que nous voulions exécuter cinq opérations asynchrones, chacune ne pouvant s'exécuter que lorsqu'une autre est terminée. Si nous devions coder ceci, nous aurions quelque chose de ce genre :

doSomething1(() => {
    doSomething2(() => {
        doSomething3(() => {
            doSomething4(() => {
                doSomething5(() => {
                    // final action
                });
            });
        });
    });
});

Lorsque les callbacks imbriqués ont de nombreuses lignes de code à exécuter, ils deviennent nettement plus complexes et illisibles. À mesure que votre projet JavaScript prend de l'ampleur et devient plus complexe, cet effet s'accentuera, jusqu'à devenir ingérable. C'est pour cette raison que les développeurs n'utilisent plus les callbacks pour gérer les opérations asynchrones. Pour améliorer la syntaxe de notre code asynchrone, nous pouvons utiliser des promesses à la place.

Utiliser des promesses pour une programmation asynchrone concise

Une promesse est un objet JavaScript qui retournera une valeur à un moment donné dans le futur. Les fonctions asynchrones peuvent retourner des objets de promesse au lieu de valeurs concrètes. Si nous recevons une valeur dans le futur, nous disons que la promesse a été remplie. Si nous recevons une erreur dans le futur, nous disons que la promesse a été rejetée. Sinon, la promesse est toujours en cours de traitement dans un état en attente.

Les promesses prennent généralement la forme suivante :

promiseFunction()
    .then([ Callback Function for Fulfilled Promise ])
    .catch([ Callback Function for Rejected Promise ])

Comme indiqué dans ce modèle, les promesses utilisent également des fonctions de callback. Nous avons une fonction callback pour la méthode then(), qui est exécutée lorsqu'une promesse est remplie. Nous avons également une fonction callback pour la méthode catch() pour gérer toute erreur qui se produit pendant que la promesse est exécutée.

Faisons l'expérience directe des promesses en réécrivant notre programme Studio Ghibli en utilisant des promesses à la place.

Axios est un client HTTP basé sur une promesse pour JavaScript, alors allons-y et installons-le :

  • npm i axios --save

Maintenant, avec l'éditeur de texte de votre choix, créez un nouveau fichier promiseMovies.js :

  • nano promiseMovies.js

Notre programme fera une requête HTTP avec axios et utilisera ensuite une version spéciale de fs basée sur les promesses pour enregistrer dans un nouveau fichier CSV.

Tapez ce code dans promiseMovies.js pour que nous puissions charger Axios et envoyer une requête HTTP à l'API de films :

promiseMovies.js
const axios = require('axios');

axios.get('https://ghibliapi.herokuapp.com/films');

Dans la première ligne, nous chargeons le module axios, en stockant la fonction retournée dans une constante appelée axios. Nous utilisons ensuite la méthode axios.get() pour envoyer une requête HTTP à l'API.

La méthode axios.get() retourne une promesse. Enchaînons cette promesse pour pouvoir imprimer la liste des films de Ghibli sur la console :

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        response.data.forEach(movie => {
            console.log(`${movie['title']}, ${movie['release_date']}`);
        });
    })

Analysons ce qui se passe. Après avoir fait une requête HTTP GET avec axios.get(), nous utilisons la fonction then(), qui est exécutée seulement lorsque la promesse est remplie. Dans ce cas, nous imprimons les films à l'écran comme nous l'avons fait dans l'exemple des callbacks.

Pour améliorer ce programme, ajoutez le code mis en évidence pour écrire les données HTTP dans un fichier :

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })

Par ailleurs, nous importons une nouvelle fois le module fs. Remarquez comment, après l'importation de fs, nous avons .promises. Node.js comprend une version basée sur les promesses de la bibliothèque fs basée sur les callback, de sorte que la rétrocompatibilité n'est pas rompue dans les anciens projets.

La première fonction then() qui traite la requête HTTP appelle maintenant fs.writeFile() au lieu d'imprimer dans la console. Puisque nous avons importé la version de fs basée sur la promesse, notre fonction writeFile() renvoie une autre promesse Ainsi, nous ajoutons une autre fonction then() pour le cas où la promesse writeFile() serait remplie.

Une promesse peut retourner une nouvelle promesse, ce qui nous permet d'exécuter des promesses les unes après les autres. Cela nous permet d'effectuer de multiples opérations asynchrones. C'est ce qu'on appelle l'enchaînement des promesses, et c'est analogue à l'imbrication des callbacks. Le second then() n'est appelé qu'après que nous ayons réussi à écrire dans le fichier.

Remarque : dans cet exemple, nous n'avons pas vérifié le code de statut HTTP comme nous l'avions fait dans l'exemple de callback. Par défaut, axios ne remplit pas sa promesse s'il reçoit un code de statut indiquant une erreur. Pour autant, nous n'avons plus besoin de le valider.

Pour terminer ce programme, enchaînez la promesse avec une fonction catch(),comme mis en évidence dans ce qui suit :

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })
    .catch((error) => {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    });

Si une promesse n'est pas remplie dans la chaîne des promesses, JavaScript va automatiquement à la fonction catch(), si elle a été définie. C'est pour cette raison que nous n'avons qu'une seule clause catch(), même si nous avons deux opérations asynchrones.

Assurons-nous que notre programme produit le même résultat en exécutant :

  • node promiseMovies.js

Dans votre dossier ghibliMovies, vous verrez le fichier promiseMovies.csv, qui contient :

promiseMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Avec les promesses, nous pouvons écrire un code beaucoup plus concis qu'en utilisant uniquement des callbacks. La chaîne de promesses des callbacks est une option plus propre que l'imbrication des callbacks. Cependant, à mesure que nous lançons des appels plus asynchrones, notre chaîne de promesses devient plus longue et plus difficile à maintenir.

La verbosité des callbacks et des promesses vient de la nécessité de créer des fonctions quand nous avons le résultat d'une tâche asynchrone. Il serait préférable d'attendre un résultat asynchrone et de le mettre dans une variable en dehors de la fonction. De cette façon, nous pouvons utiliser les résultats dans les variables sans avoir à créer une fonction. Nous pouvons y parvenir grâce aux mots clés async et await.

Écrire JavaScript avec async/await.

Les mots-clés async/await offrent une syntaxe alternative lorsque l'on travaille avec des promesses. Au lieu d'avoir le résultat d'une promesse disponible dans la méthode then(), le résultat est renvoyé sous forme de valeur comme dans toute autre fonction. Nous définissons une fonction avec le mot-clé async pour dire à JavaScript qu'il s'agit d'une fonction asynchrone qui renvoie une promesse. Nous utilisons le mot-clé await pour dire à JavaScript de retourner les résultats de la promesse au lieu de retourner la promesse elle-même lorsqu'elle est remplie.

En général, l'utilisation de async/await ressemble à ceci :

async function() {
    await [Asynchronous Action]
}

Voyons comment l'utilisation d’async/await peut améliorer notre programme Studio Ghibli. Utilisez votre éditeur de texte pour créer et ouvrir un nouveau fichier asyncAwaitMovies.js :

  • nano asyncAwaitMovies.js

Dans votre fichier JavaScript nouvellement ouvert, commençons par importer les mêmes modules que nous avons utilisés dans notre exemple de promesse :

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

Les importations sont les mêmes que celles de promiseMovies.js, car async/await utilise des promesses.

Maintenant, nous utilisons le mot-clé async pour créer une fonction avec notre code asynchrone :

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {}

Nous créons une nouvelle fonction appelée saveMovies() mais nous incluons async au début de sa définition. C'est important, car nous ne pouvons pas utiliser le mot-clé await dans une fonction asynchrone.

Utilisez le mot-clé await pour faire une requête HTTP qui reçoit la liste des films de l'API de Ghibli :

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
}

Dans notre fonction saveMovies() nous faisons une requête HTTP avec axios.get() comme précédemment. Cette fois, nous ne l'enchaînons pas avec une fonction then(). Au lieu de cela, nous ajoutons await avant qu'il ne soit appelé. Lorsque JavaScript voit await, il n'exécute le code restant de la fonction que lorsqu’axios.get() a terminé son exécution et défini la variable réponse. L'autre code enregistre les données du film afin que nous puissions écrire dans un fichier.

Écrivons les données de film dans un fichier :

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
    await fs.writeFile('asyncAwaitMovies.csv', movieList);
}

Nous utilisons également le mot-clé await lorsque nous écrivions dans le fichier avec fs.writeFile().

Pour remplir cette fonction, nous devons capturer les erreurs que nos promesses peuvent lancer. Faisons cela en encapsulant notre code dans un bloc try/catch :

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

Comme les promesses peuvent échouer, nous enveloppons notre code asynchrone d'une clause try/catch. Cela permettra de capturer toutes les erreurs qui sont lancées lorsque la requête HTTP ou les opérations d'écriture de fichiers échouent.

Enfin, appelons notre fonction asynchrone saveMovies() de sorte qu'elle sera exécutée lorsque nous exécutons le programme avec node

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

saveMovies();

Au premier coup d'œil, cela ressemble à un bloc de code JavaScript synchrone typique. Il a moins de fonctions en circulation, ce qui lui donne un aspect un peu plus soigné. Ces petits ajustements rendent le code asynchrone avec async/await plus facile à gérer.

Testez cette itération de notre programme en entrant ceci dans votre terminal :

  • node asyncAwaitMovies.js

Dans votre dossier ghibliMovies, un nouveau fichier asyncAwaitMovies.csv sera créé avec le contenu suivant :

asyncAwaitMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Vous avez maintenant utilisé les fonctionnalités de JavaScript async/await pour gérer le code asynchrone.

Conclusion

Dans ce tutoriel, vous avez appris comment JavaScript gère l'exécution des fonctions et les opérations asynchrones avec la boucle d'événements. Vous avez ensuite écrit des programmes qui ont créé un fichier CSV après avoir fait une requête HTTP pour des données de film, en utilisant diverses techniques de programmation asynchrone. Tout d'abord, vous avez utilisé l'approche obsolète basée sur les callbacks. Vous avez ensuite utilisé des promesses, et enfin async/await pour rendre la syntaxe de promesse plus succincte.

Grâce à votre compréhension du code asynchrone avec Node.js, vous pouvez maintenant développer des programmes qui tirent profit de la programmation asynchrone, comme ceux qui reposent sur des appels d'API. Consultez cette liste d’API publiques. Pour les utiliser, vous devrez faire des requêtes HTTP asynchrones, comme nous l'avons fait dans ce tutoriel. Pour une étude plus approfondie, essayez de construire une application qui utilise ces API , afin de mettre en pratique les techniques que vous avez apprises ici.

0 Comments

Creative Commons License