Async / Await avec ES8

Les fonctions async / await permettent d'écrire du code asynchrone non bloquant aussi facilement que du code synchrone. Cette méthode remplace les célèbres callbacks et complète les promesses dans l'écriture de code asynchrone.

Les callbacks, bonne idée mais syntaxe difficile

Les callbacks étaient au coeur de la programmation asynchrone en JavaScript. Un callback désigne la fonction appelée une fois que les opérations sont terminées.

Dans l'exemple suivant, une requête est lancée pour récupérer le contenu de "https://developer.mozilla.org/". Une fois l'URL trouvée, la page est affichée sous forme de texte.

const xhr = new XMLHttpRequest()
const method = "GET"
const url = "https://developer.mozilla.org/"

xhr.open(method, url, true)
xhr.onreadystatechange = () => {
  if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
    console.log(xhr.responseText)
  }
}
xhr.send()

Les callbacks ne sont pas simples à utiliser ou à relire. La complexité croit quand les callbacks sont chaînés ou imbriqués.

function1(args, () => {
  function2(args, () => {
    function3(args, () => {
      function4(args, () => {
        console.log('done!')
      })
    })
  })
})

L'invasion des promesses

Introduites par ES6, les promesses simplifient grandement la gestion de l'asynchrone.

L'objet Promise représente l'état asynchrone d'une opération et contient une valeur. Une promesse est toujours dans l'un de ces 3 états :

  • En attente (pending) : état initial, l'opération est en cours de résolution
  • Validée (fulfilled) : l'opération s'est terminée avec succès
  • Rejetée (rejected) : l'opération s'est terminée par un échec

Une promesse « en attente » peut passer à l'état « validée » ou à l'état « rejetée ». Une fois « validée » ou « rejetée », son état est définitif. En fonction du résultat de la promesse, la suite du code est décrite dans les fonctions .then() en cas de succès ou .catch() en cas d'échec

Voyons un exemple de code. Le code suivant utilise la promesse fetch pour récupérer le prix d'un objet. Si fetch retourne le prix attendu, il est affiché dans la console. Si fetch retourne une erreur, un message d'erreur est affiché.

fetch('/api/price')
  .then(response => {
    // Retourne une nouvelle promise, qui convertit le résultat de la
    // requète en texte brut
    return response.text()
  })
  .then(price => { 
    // Affiche le prix dans la console
    console.log(`L’objet coûte : ${price} €.`)
  })
  .catch(error => {
    // Affiche une erreur car le prix n'a pas été trouvé
    console.log(`${error} - L'objet n'a pas été trouvé.`)
  })

L'écriture, la lecture et l'utilisation des fonctions asynchrones sont facilitées. En revanche, le code reste lourd à écrire avec un enchaînement de then et de fonctions (malgré la syntaxe arrow function qui allège le code). C’est là que la syntaxe async / await entre en jeu !

ES8 async / await functions

async / await est une fonctionnalité JavaScript qui rend l'utilisation des fonctions asynchrones plus agréable et plus facile à comprendre.

Le mot clef async, placé devant une fonction permet de déclarer une fonction asynchrone :

  • async encapsule le résultat de la fonction dans une promesse
  • async permet l'utilisation du mot clef await dans le corps de la fonction

Le mot clef await, placé devant une promesse permet de mettre en pause l'exécution du code :

  • await bloque l'exécution du code tant que la promesse n'est pas terminée
  • await fonctionne avec les promesses mais pas avec les callbacks
  • await ne peut être utilisé que dans une fonction asynchrone

Dans l'exemple suivant, async permet de déclarer la fonction fetch comme asynchrone. Tant que l'appel à fetch n'est pas terminé, l'exécution du code est en pause. Une fois le prix retourné par le serveur, il est affiché. Dans la cas contraire, une erreur s'affiche.

async function printPrice () => {
  try {
    const price = await fetch('/api/price')
    console.log(`L'objet coûte : ${price} €.`)
  } catch (error e) {
    // Affiche une erreur car le prix n'a pas été trouvé
    console.log(`${error} - L'objet n'a pas été trouvé.`)
  }
}

Avec cette syntaxe, les chaînes de promesses n'existent plus. Vous pouvez désormais écrire du code asynchrone avec une syntaxe proche du code synchrone. Le code de la fonction est en "pause" mais le reste de votre code JavaScript continue d'être exécuté, on conserve donc tous les avantages de l'asynchrone sans pour autant faire de compromis sur la lisibilité du code.

Support navigateur

async / await est désormais utilisable dans la majorité des navigateurs à l’exception d’Internet Explorer 11 (IE11) : caniuse.com.

Support navigateur des "async functions"

Pour tous les anciens navigateurs, il faut utiliser un transpileur comme Babel.

Découvrez nos formations JavaScript