Appearance
Fetch & asynchrone
Présentation
Dans ce chapitre, nous verrez comment il est possible en Javascript de dialoguer avec un serveur distant via une API en utilisant la commande Javascript native fetch. Cela nous amènera à parler de la gestion de l’asynchrone en Javascript.
Gérer des commandes asynchrones
Distinguer synchrone et asynchrone
Dans la majorité des programmes Javascript, l’essentiel des commandes sont dites synchrones. Cela signifie qu’elles sont exécutées dans l’ordre dans lequel elles sont écrites dans un script ou une fonction par exemple. Cet ordre de lecture est important car lorsque l’on écrit par exemple :
jsx
let myVar = 2
console.log(myVar)Nous nous attendons ici à ce que la valeur 2 soit affichée dans notre console. Cela sous-entend donc que nous nous attendons à ce que ma première commande soit bien terminée avant que la seconde soit exécutée.
Un comportement synchrone induit donc une chose : chaque commande bloque l’exécution des suivantes, le temps qu’elle soit complètement terminée. Cela peut paraître dérisoire, lorsque l’on considère des commandes s’exécutant en moins d’un millième de seconde. Mais certaines commandes peuvent prendre un temps plus conséquent avant de se finaliser.
C’est le cas notamment des échanges de données entre machines, lorsque que l’on télécharge des fichiers, ou que l’on dialogue simplement avec une API. Le temps entre la demande et le retour, ou le transfert complet peut prendre en temps de l’ordre de la seconde, voire de la minute selon les cas. Par ailleurs, ce temps dépend de facteurs extérieurs à notre machine, comme l’état du réseau par exemple, et est donc difficile à estimer.
Il peut alors devenir problématique d’arrêter notre programme pendant un temps conséquent dans l’attente d’une commande spécifique. C’est pourquoi les commandes asynchrones ont été inventées.
Contrairement aux commandes synchrones, notre programme n’attend pas la résolution des commandes asynchrones pour passer à la suite. Il est donc important que la suite des commandes ne compte pas sur le fait qu’une commande asynchrone se soit bien terminée avant son exécution, car ce ne sera pas nécessairement le cas.
Bien sûr, il est possible de définir une fonction qui sera exécutée uniquement lorsque l’exécution de la commande asynchrone sera terminé. Mais à nouveau, nous n’aurons aucune certitude sur le moment où cette fonction sera lancée par rapport au reste du programme.
INFO
💡 Les commandes asynchrones ne se limitent pas aux échanges entre machines. Elles sont également utiles pour gérer des calculs particulièrement longs ne devant pas bloquer le bon déroulement d’une l’application.
Créer une commande asynchrone
Promise
En javascript, il est possible de créer une commande asynchrone en utilisant la classe native Promise. Il suffit d’instancier une promise en lui fournissant la fonction à exécuter de façon asynchrone :
jsx
const myPromise = new Promise(
(resolve, reject) => {
veryLongTimeFunc()
resolve()
}
)La fonction asynchrone reçoit deux arguments : resolve et reject. Il s’agit de deux fonctions à exécuter à la fin de notre commande asynchrone. resolve est à appeler pour signaler que la commande s’est bien passée. reject doit être appelée si la commande n’a pas abouti correctement.
Pour exécuter la commande asynchrone, il suffit ensuite de l’appeler comme une fonction :
jsx
myPromise() // asynchrone functionVous pouvez alors exécuter la méthode then sur votre appel pour indiquer la fonction à exécuter une fois la commande asynchrone terminée. La méthode except vous permettra d’indiquer quoi faire si la commande n’aboutit pas :
jsx
myPromise().then(
() => { console.log("Done !")}
).except(
() => { console.log("Failed !") }
)La fonction fournie à then sera exécutée lorsque resolve sera appelée, de la même façon la fonction fournie à except sera exécutée si reject est appelée.
INFO
💡 Vous pouvez bien sûr passer des arguments à votre promise pour les récupérer dans votre fonction asynchrone. Vous pouvez aussi fournir des données de votre commande asynchrone à la fonction exécutée ensuite grâce à la fonction resolve. Plus d’infos dans la documentation de la classe Promise.
async / await
Depuis la version ES8 de Javascript, une nouvelle syntaxe permet de créer des fonctions asynchrones. Elle permet de déclarer une fonction asynchrone simplement en utilisant le mot-clé async :
jsx
async myFunction() { console.log("async message") }
const myFunction2 = async () => { console.log("other async message") }Ces fonctions s’appellent ensuite comme n’importe quelle autre fonction.
Si vous souhaitez attendre la résolution de la fonction asynchrone, il vous suffira d’ajouter le mot-clé await avant son appelle dans une autre fonction :
jsx
const myFunction = async () => {
const data = await myAsyncFunction()
console.log(data) // Done
}
const myAsyncFunction = async () => {
veryLongTimeFun()
return "Done"
}Grace au mot-clé await, la fonction myFunction attendra la résolution de la fonction myAsyncFunction avant de passer aux commandes suivantes. myFunction devient donc nécessairement à son tour asynchrone. Une fonction utilisant await devra donc toujours être déclarée avec async.
Il n’est donc pas rare d’avoir une série de fonctions async/await s’appelant les unes les autres, de même que des promises appelant d’autres promises.
WARNING
⚠️ async/await ne permet pas de gérer les commandes non abouties comme le font les promises avec reject. Pour gérer la réussite de la commande, il est possible de jouer sur le contenu retourné par la fonction ou de lever une exception qui sera attrapée par un système de try/catch par exemple.
La commande fetch
Emettre une requête
fetch est une commande native en Javascript vous permettant d’émettre des requêtes HTTP afin de dialoguer avec une API REST par exemple.
Pour l’utiliser, il vous suffit d’indiquer en argument l’URL de votre requête :
jsx
fetch("https://www.my-url.com")La fonction retourne de façon asynchrone la réponse de la requête. Vous pouvez récupérer la réponse en utilisant les syntaxes des promises ou async/await :
jsx
fetch("https://www.my-url.com").then((response) => foo(response))
// OR
response = await fetch("https://www.my-url.com")Par défaut, la fonction emmétra une requête du type GET.
Vous pouvez passer en second argument de la fonction de nombreux paramètres pour émettre une requête d’un autre type, fournir des données dans le corps de la requête, ou encore modifier l’en-tête de la requête pour fournir un jeton d’authentification par exemple :
jsx
fetch(
"https://www.my-url.com",
{
method: "POST", // or GET, PUT, DELETE, etc.
headers: {
"Content-Type": "application/json" // to have JSON content in body
'Authorization': 'Bearer ' + myToken, // set JWT token
},
body: JSON.stringify(myData) // set JSON data in request body
}
)INFO
💡 Il existe plusieurs librairies disponibles sur NPM pour gérer les échanges HTTP avec des APIs. Axios est un exemple assez populaire. Libre à vous de passer par l’une de ces librairies si vous préférez.
Gérer une réponse
La fonction fetch retourne la réponse de la requête sous la forme d’un objet. Cet objet présente de nombreuses informations sur l’état de la réponse.
Vous pouvez notamment connaître le code de statut de l’échange grâce à l’attribut status . Ce statut à une valeur comprise entre 200 et 299 s’il s’est bien déroulé (200 si votre requête vise uniquement à récupérer des données). Votre statut peut comporter un nombre très divers de valeurs, en fonction des problèmes rencontrés, dont des statuts utilisés spécifiquement par le serveur de l’API. Les codes les plus répandus sont 404 si le serveur n’a pas été trouvé à partir de l’URL donnée, et 500 si la requête à généré une erreur en interne du serveur qui a réceptionné la requête.
Si votre requête a bien abouti, vous pouvez récupérer le contenu du corps de la réponse grâce à la méthode json() si le corps est au format JSON, ou la méthode text() pour obtenir le corps sous forme d’une chaîne de caractères :
jsx
const response = await fetch("https://www.my-url.com")
if (response.status == 200) {
const data = response.json()
} else {
new Error(response.statusText)
}Aller plus loin
Sur la commande fetch : la documentation

