Async Await et Promises

logo

Je vais parler dans cet article des mots clés « async await’ qui sont des mots clés utilisés en Javascript pour gérer les traitements asynchrones.

On va commencer par faire un tour des différentes façons de faire de l’asynchrone pour afin de bien comprendre les pour et les contres de cette solution.

Retour sur les callbacks

Supposons qu’on souhaite faire un appel asynchrone, par exemple, appeler une API pour charger une liste d’informations, on peut le faire en utilisant des fonctions de callback, cette solution est la plus simple (et vielle) et vous l’avez certainement déjà utilisé, voici un exemple de ce que ça donne :

// ...
function loadProductsList(onLoadComplete) {
    doCallApi(function(response){
            onLoadComplete(response.data)
        })
}

loadProductsList(function(data) {
    console.log('The data is ', data)
}) 

on déclare une fonction loadProductsList qui accepte une fonction qu’on nommera onLoadComplete, cette fonction sera appelée quand le contenu est disponible.

Ensuite lors de l’appel de loadProductsList on lui passe en argument une fonction qui va traiter la réponse renvoyée.

Cependant cette solution présente tape ces limites quand on veux faire des appels imbriqués, on tombe alors bien souvent sur ce qu’on appele le Callback hell.

Les promises

La deuxième façon de gérer les appels asynchrones est de passer par des promises, voici ci dessous un exemple de code.

function loadProductsList(){
    return new Promise(resolve => {
        doCallApi((response) => {
            resolve(response.data)
        })
    })
}

loadProductsList()
    .then((data) => {
        console.log('The data is ', data)
    })

Dans cet exemple vous remarquerez que la fonction loadProductsList retourne un objet de type Promise, du coup lors de l’appel on configure cette promise avec le callback à executer avec la méthode then de la promise.

l’avantage de cette méthode est qu’on peux enchainer les traitements asynchrones de la manière suivante :

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => response.json()) // oui ça retourne bien une Promise
  .then(json => console.log(json))

On a donc une syntaxe plus propre, mais on ne résoud toujours pas le problème d’appels imbriqués, regardons ce qui ce passe dans l’exemple ci dessous :

function sellProduct(productId, customerId) {
    fetch(`/product/price?id=${productId}`)
    .then((price) => {
        fetch(`/customer/balance?id=${customerId}`)
        .then((balance) => {
            if(balance > price) {
                fetch(`/product/sell?product=${productId}&customer=${customerId}`)
                .then((status) => {
                    if(status === 'OK') {
                        alert('YAAAAY')
                    }
                })
            }
        })
    })
}

dans ce bout de code on souhaite vendre un produit seulement si le client a assez de solde, pour y arriver on a besoin de faire 3 appels asynchrones.

Comme vous le remarquez on se retrouve toujours avec le même problème de callback hell pour ce cas, voyons donc comment on peut corriger ça avec async await.

Async await

voici un exemple d'utilisation d'async await

async function getContent() {
  return "deferred content";
}

const result = getContent();
console.log(result);

Si vous executez ce bout de code, vous remarquerez que le console.log affiche une Promise, et non un texte, on déduit donc que toute fonction préfixée par async retourne toujour une promise.

ça suppose qu'on peut résoudre la promise ainsi :

async function getContent() {
  return "deferred content";
}

const res = getContent()
res.then((content) => console.log(content))

l'utilisation du then nous permet de voir le contenu.

En plus de cette propriété, async nous permet d'utiliser le mot clé await, voici un exemple d'utilisation :

async function testWithAsync() {
  let response = await fetch('https://jsonplaceholder.typicode.com/posts/1')
  const result = await response.json()
  console.log(result)
}

testWithAsync()

await va bloquer l'execution du code jusqu'a ce que la promise soit résolue, ce qui facilite la lecture du code.

voyons comment notre code deviens avec async await :

async function sellProduct(productId, customerId) {
    const price = await fetch(`/product/price?id=${productId}`)
    const balance = await fetch(`/customer/balance?id=${customerId}`)
    if(balance > price) {
        const status = await fetch(`/product/sell?product=${productId}&customer=${customerId}`)
        if(status === 'OK') {
            alert('YAAAAY')
        }
    }

}

On constate que le code deviens plus lisible et on comprends tout de suite l'intention derrière.

Mais avant d'abandonner complètement les Promises, il reste quand même un use case où ils ont l'avantage, et c'est les appels parallèles.

Pour mieux illustrer ça, on va prendre l'exemple d'une page qui affiche des news, ainsi que les amis connectés.

avec async await ça devrait donner quelque chose comme ça:

async function loadPage(){
    const news = await fetch('/news')
    showNews(news)
    const friends = await fetch('/friends')
    showFriends(friends)
}

Bien que le code soit simple, l'expérience utilisateur ne va pas être top, car le deuxième appel ( /friends ) ne sera appelé qu'après la fin du premier.

Le deuxième problème est que le deuxième appel ne sera jamais exécuté si le premier tombe en erreur.

Regardons maintenant le même exemple avec les promises :

function loadPage() {
    fetch('/news').then(showNews)
    fetch('/friends').then(showFriends)
}

vous remarquez ici que nos appels sont exécutés en parallèle et sans dépendances entre eux.
en plus de ça on a une meilleure expérience utilisateur grâce à l’amélioration au temps de réponse.

Voila pour les async await, n’hésitez pas à poser vos questions si besoin dans la zone de commentaires.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *