Cours Node.js Cours Node.js - Partie 2 - Programmation asynchrone et requête à d'autres APIs

Dans la partie précédente, nous avons appris:

Jusqu’à présent, nous n’avons appelé et défini que des fonctions synchrones. C’est à dire que Node.js exécute ces fonctions de manière séquentielle.

L’appel à une fonction synchrone bloque l’exécution du programme jusqu’à la fin du traitement qu’elle effectue. Lorsqu’un traitement a une durée indéterminée, il est inacceptable qu’un serveur attende la fin de ce traitement, car cela l’empêcherait de répondre à d’autres requêtes pendant ce temps là. C’est pour éviter cela que nous allons apprendre à utiliser des fonctions asynchrones.

Objectifs de cette partie

Prérequis:

Durée estimée: 4 heures.


Exercice 1 - Envoi de requête à une API externe

Dans la partie précédente, nous avons développé un serveur d’API HTTP: un programme qui répond à des requêtes.

Dans cet exercice, nous allons écrire un programme qui émet une requête vers l’API HTTP d’un autre serveur. Nous allons donc développer un client d’API, afin de découvrir le concept d’appel de fonction asynchrone.

👉 Exerciseur en ligne: Envoi de requête à une API externe

Indice: consulter la documentation de la fonction https.get().


Exercice 2 - Gestion d’erreurs de callback

Dans l’exercice précédent, nous sommes naïvement partis du principe que la requête fonctionnerait systématiquement. Cela revient à espérer que l’API interrogée ne sera jamais indisponible et que l’utilisateur de notre programme sera connecté à Internet de manière fiable. En réalité, un programme n’est jamais à l’abri de traitements qui ne se passent pas comme prévu, et c’est au développeur de prévoir et traiter correctement ces cas d’erreur, afin de ne pas laisser l’utilisateur dans l’embarras face à un problème qu’il ne saura pas résoudre.

Dans cet exercice, nous allons volontairement envoyer une requête sur un serveur qui n’existe pas, et afficher l’erreur retournée par https.get() dans la sortie standard.

👉 Exerciseur en ligne: Gestion d’erreurs de callback

Indice: renseignez-vous sur la manière dont Node.js retourne les erreurs dans ses fonctions de requêtes HTTP/HTTPS: référence de http.request().


Exercice 3 - Requête à l’aide de Promise

Dans les exercices 1 et 2, nous avons envoyé une requête HTTP GET à l’aide de la fonction https.get() fournie par Node.js. Pour définir le traitement de la réponse à cette requête, nous avons du définir deux fonctions. Passée en paramètre de l’appel à https.get(), la première fonction permet de récupérer un objet res. Nous avons défini une deuxième fonction et l’avons passée en paramètre de l’appel de fonction res.on('data', ...), afin de décrire que faire des données qui sont récupérées progressivement depuis le serveur, en réponse à la requête que nous lui avons envoyée.

Ces deux fonctions que nous avons définies s’appellent des callbacks, car nous les avons conçues pour qu’elles soient rappelées (c’est la traduction française de l’expression call back) par les fonctions auxquelles nous les avons passé en paramètre.

Les fonctions fournies par la bibliothèque standard de Node.js s’appuient généralement sur ce principe de callback pour être informé d’événements asynchrones. Dans l’exercice précédent, l’événement asynchrone qui nous intéressait était la réception d’une réponse à notre requête.

Le concept de promesse (en anglais: Promise; cf javascript.info et Référence MDN) a été intégré au langage JavaScript afin de simplifier le séquençage d’appels asynchrones, et améliorer leur lisibilité en évitant le “callback hell”.

Dans cet exercice, nous allons utiliser une promesse pour récupérer le résultat d’un appel à une fonction asynchrone. Pour cela, nous allons utiliser la bibliothèque node-fetch.

👉 Exerciseur en ligne: Requête à l’aide de Promise

Indice: consulter la documentation de node-fetch pour savoir comment l’utiliser. (cf lien fourni ci-dessus)


Exercice 4 - Gestion d’erreurs de Promise

Encore une fois, nous avons implémenté une version naïve de notre requête, en partant du principe que celle-ci se passerait comme prévu.

Dans cet exercice, nous allons volontairement envoyer une requête sur un serveur qui n’existe pas, et afficher dans la sortie standard l’erreur (aussi appelée exception) retournée par la promesse de fetch().

👉 Exerciseur en ligne: Gestion d’erreurs de Promise

Indice: consulter les références fournies en fin de page, pour savoir comment récupérer les erreurs lorsqu’on appelle une fonction qui retourne une Promise.


Exercice 5 - Requête à l’aide de await

Le mot clé await a été intégré au langage JavaScript pour simplifier et rendre encore plus lisible l’appel de fonctions asynchrones à base de Promesses.

Dans cet exercice, nous allons utiliser await au lieu de .then() pour afficher la réponse retournée par l’appel à fetch().

👉 Exerciseur en ligne: Requête à l’aide de await

Indice: Sachant que await ne peut être employé qu’au sein d’une fonction async, vous allez devoir définir une fonction async et l’appeler dans la foulée pour que la requête soit envoyée. Cf l’exemple fourni dans l’annexe suivante: Fonctions synchrones VS asynchrones.


Exercice 6 - Gestion d’erreurs de await

Encore une fois, nous avons implémenté une version naïve de notre requête, en partant du principe que celle-ci se passerait comme prévu.

Dans cet exercice, nous allons volontairement envoyer une requête sur un serveur qui n’existe pas, intercepter l’erreur renvoyée par await fetch() et afficher cette erreur dans la sortie standard.

👉 Exerciseur en ligne: Gestion d’erreurs de await

Indice: Lorsqu’une fonction asynchrone est appelée avec await, les erreurs sont interceptées de la même manière que lorsqu’on appelle une fonction synchrone: à l’aide d’un bloc try-catch. Cf l’exemple fourni dans l’annexe suivante: Fonctions synchrones VS asynchrones.


Exercice 7 - Requête complète

Dans les exercices précédents, nous avons employé les 3 façons d’effectuer un appel de fonction asynchrone: callback, Promise et await. Pour chacune de ces façons de faire, nous avons écrit un programme affichant la réponse quand aucune erreur ne survient, et un exercice séparé ne traitant que les cas d’erreurs.

Nous avons vu que l’usage de await permet d’appeler des fonctions asynchrones et de gérer leurs erreurs comme si ces fonctions étaient synchrones. Ce sucre syntaxique est à privilégier, pour la lisibilité et la robustesse qu’il apporte au code.

Dans cet exercice, nous allons combiner la solution des exercices 5 et 6 afin d’obtenir un programme qui affichera la réponse à la requête ou l’erreur obtenue, selon le cas.

Afin de nous permettre de tester les deux cas à la demande, ce programme:

Par exemple:

node get.js https://jsonplaceholder.typicode.com/photos # affichera la réponse
node get.js https://serveur-inexistant.xyz # affichera un message d'erreur

👉 Exerciseur en ligne: Requête complète

Indice: How to parse command line arguments | Node.js.


Exercice 8 - Requête complète, en local

Dans les exercices précédents, votre serveur était exécuté et testé dans le cloud, pour vous aider à focaliser votre attention sur l’écriture du code asynchrone.

Vous allez maintenant re-créer, exécuter et tester le programme de l’exercice 7 sur votre propre ordinateur, en créant un fichier get.js et en ajoutant ses dépendances dans package.json à l’aide de npm.

L’objectif est de pouvoir tester votre programme depuis votre terminal bash, comme montré à la fin de l’énoncé précédent.


Exercice 9 - Requête complète, sur Heroku

Maintenant que votre programme fonctionne localement, déployez le en production, sur Heroku.

Comment pouvons-nous tester que la requête fonctionne ?

À l’aide d’Express, ajoutez une route qui effectuera la requête à la demande et retournera le résultat (réponse ou message d’erreur) en réponse de toute requête adressée à cette route.


Prise de recul: appels synchrones et asynchrones

Questions auxquelles savoir répondre:


Pour aller plus loin

Ressources sur l’exécution de code asynchrone

Manières d’effectuer une requête HTTP depuis Node.js

Il existe plusieurs manières d’effectuer des requêtes HTTP depuis Node.js.

Notamment:

Quelle solution préférez-vous ? Pourquoi ?