Nous avons vu précédemment comment récupérer des informations sur un serveur distant grâce à fetch().

Par exemple, pour récupérer les informations d'une personne fictive, il est possible d'effectuer un appel à l'API de randomuser.me/api:

fetch("https://randomuser.me/api?results=1")
  .then(response => response.json())
  .then(data => console.log(data));

Ce code affiche dans la console un objet contenant les différentes informations d'une personne fictive:

{
  gender: "female",
  name: {
    first: "Eléonore",
    last: "Gonzalez",
    title: "Ms",
  },
  nat: "FR"
}

Imaginons que des informations supplémentaires sur le pays (FR 🇫🇷) de cette personne soient demandées. Un appel à un deuxième API restcountries.eu spécifiant le pays désiré serait requis afin d'obtenir ces informations.

Le code ressemblerait alors à ceci:

function getPeople() { 
  fetch("https://randomuser.me/api?results=1")
    .then(responseUser => responseUser.json())
    .then(dataUser => {
      const user = dataUser.results[0];
        
      fetch(`https://restcountries.eu/rest/v2/alpha/${user.nat}`)
        .then(responseCountry => responseCountry.json())
        .then(dataCountry => {
          console.log({
            user: user,
            country: dataCountry
          });
        });
    });
}

getPeople();

Et le résultat dans la console à ceci :

{
  user: {
    gender: "female",
    name: {
      first: "Eléonore",
      last: "Gonzalez",
      title: "Ms",
    },
    nat: "FR"
  },
  country: {
    capital: "Paris",
    name: "France",
    population: 66710000,
    region: "Europe"
  }
}

Le tout combiné dans un exemple réel donnerait quelque chose comme ceci:

😬

Ce code n’est pas mauvais. Il fonctionne, mais certains éléments donnent envie de grincer des dents.

Remarquez comment les .then() sont imbriqués les uns à la suite des autres. Imaginez qu'avec les informations du pays, vous deviez appeler un autre API supplémentaire et ainsi de suite, vous, vous retrouveriez avec ce que l'on appelle un callback hell 👿, soit plusieurs callbacks imbriqués les uns dans les autres, rendant la lecture du code et le débogage ardu.

async

Il est heureusement possible de rendre ce code plus lisible et plus facilement déboguable.

Pour ce faire, une fonction doit retournee un résultat de façon asynchrone, bref que son return soit une promesse (promise) et non une valeur directement.

Le mot-clé async préfixant une fonction permet d'effectuer cette conversion. Par exemple, une fonction synchrone de base ressemble à ceci:

function demo() {
  return "Résultat";
}

console.log(demo()); // Résultat

Pour la convertir en fonction asynchrone, il suffit de la préfixer avec async:

async function demo() {
  return "Résultat";
}

console.log(demo()); // [object Promise] {}

Pour accéder à la valeur dans la promesse (promise), il faut attendre que celle-ci soit complétée. À ce moment, la méthode .then() est invoquée et la valeur associée à la fonction lui est passée en paramètre, il est donc finalement possible d'y accéder.

async function demo() {
  return "Résultat";
}

demo() // 👈 retourne une promesse
  .then(data => { // promesse ✅, voici sont contenu
    console.log(data) // affiche le contenu de la promesse
  });

Contrairement à une fonction synchrone de base, qui tente de répondre immédiatement, cette fonction asynchrone retourne maintenant une promesse (promise) de répondre prochainement.

Cette fonction, grâce au mot clé async, peut donc se permettre d'attendre le résultat d'un .fetch()  avant de répondre.

await

Dans l'exemple de l'usager fictif, d'on la nationalité est utilisée en deuxième temps pour allez récupérer des informations supplémentaires sur son pays, pourrait donc être refait ainsi avec une combinaison de async et await:

async function getPeople() { 
  const user = await getUser();
  const country = await getCountry(user.nat);

  return {
    user,
    country
  }
}

function getUser() {
  return fetch("https://randomuser.me/api?results=1")
    .then(response => response.json())
    .then(data => data.results[0]);
};

function getCountry(country) {
  return fetch(`https://restcountries.eu/rest/v2/alpha/${country}`)
    .then(response => response.json())
    .then(data => data);
};

getPeople()
  .then(data => {
    console.log(data);
  });

Le tout combiné dans un exemple réel donnerait quelque chose comme ceci:

Ce code est plus facile à lire et à déboguer que la version où des then() sont imbriqués.