Live Coding : Les Promesses, Async et Await

Par Nouvelle-Techno.fr le 24 octobre 2019 - Catégories : Javascript Tutoriel Live-Coding

Lire l'article sur le site d'origine

Le live de cette semaine est consacré aux promesses en Javascript.

Longtemps utilisée par l'intermédiaire de librairies, la fonctionnalité asynchrone des promesses a été introduite officiellement dans la norme ECMAScript6.

Les promesses, c'est quoi ?

Comme dans la vie courante, une promesse en javascript est un engagement à fournir une valeur. Comme dans la vie courante, cette promesse peut être tenue, dans ce cas on récupère la valeur, ou non tenue, et on réagit en conséquence.

Leur écriture est aussi plus simple que l'utilisation en chaîne de fonctions de callback imbriquées. Le code sera donc plus clair, plus facile à comprendre et à maintenir.

Créer une promesse

Pour créer une promesse, nous utiliserons le constructeur "Promise", apparu avec ES6.

Nous allons donc créer une fonction qui chargera une url distante, par exemple, et retournera un résultat uniquement quand elle aura terminé.

Pour commencer, nous allons créer la fonction et y intégrer la promesse

/**
 * Cette fonction chargera un fichier distant à l'url choisie
 * @param url 
 */
function ajaxGet(url) {
    return new Promise(function (resolve, reject) {
        // Ici nous utiliserons resolve() en cas de succès et reject() en cas d'échec
    });
}

La fonction renvoie une promesse, qui ne fait rien pour le moment, mais vous pouvez voir deux variables "resolve" et "reject" qui vont permettre de déterminer si la promesse est résolue ou si elle a échoué.

Etant donné que nous allons charger une url distante, nous utiliserons l'objet Javascript XMLHttpRequest (voir le cours sur Ajax pour plus de détails).

Notre code sera donc le suivant

/**
 * Cette fonction effectue un appel Ajax vers une url
 * @param {string} url 
 */
function ajaxGet(url){
    return new Promise(function(resolve, reject){
        // Nous allons gérer la promesse
        let xmlhttp = new XMLHttpRequest();

        xmlhttp.onreadystatechange = function(){
            // Si le traitement est terminé
            if(xmlhttp.readyState == 4){
                // Si le traitement est un succès
                if(xmlhttp.status == 200){
                    // On résoud la promesse et on renvoie la réponse
                    resolve(xmlhttp.responseText);
                }else{
                    // On résoud la promesse et on envoie l'erreur
                    reject(xmlhttp);
                }
            }
        }

        // Si une erreur est survenue
        xmlhttp.onerror = function(error){
            // On résoud la promesse et on envoie l'erreur
            reject(error);
        }

        xmlhttp.open('GET', url, true);
        xmlhttp.send(null);
    })
}

Nous retrouvons les deux cas dans les méthodes "onreadystatechange" et le statut 200 qui correspondent au succès et un autre statut aninsi que la méthode "onerror" qui correspondent à l'échec. Elles contiennent les fonctions "resolve" et "reject" qui envoient la réponse correspondante.

Utiliser une promesse, exploiter le résultat

Pour exploiter le résultat d'une promesse, nous utiliserons les méthodes "then" et "catch" qui gèreront respectivement le succès et l'échec.

Nous appellerons la promesse de la façon suivante

// On charge la fonction
ajaxGet('https://url_du_site/chemin').then(reponse => {
    // La promesse est "tenue"
    console.info('Url chargée !');
    console.log(reponse);
}).catch(erreur => {
    // Il y a une erreur
    console.error('Erreur !');
    console.dir(erreur);
});

En cas de succès, la méthode "then" sera appelée et son contenu sera exécuté. En cas d'échec, c'est la méthode "catch" qui sera exécutée.

A noter que la gestion d'échec peut également se faire dans la méthode "then" mais son exécution sera plus lente dans ce cas.

Voici le code équivalent en utilisant uniquement la méthode "then"

// On charge la fonction
ajaxGet('https://url_du_site/chemin').then(reponse => {
    // La promesse est "tenue"
    console.info('Url chargée !');
    console.log(reponse);
}, erreur => {
    // Il y a une erreur
    console.error('Erreur !');
    console.dir(erreur);
});

Chainer les promesses

Il est possible de chainer les promesses et d'exécuter des actions dès qu'une promesse est tenue. Dans ce cas, on enchainera les méthodes "then" autant de fois que nécessaire et on terminera par un unique "catch" qui gèrera l'échec de la 1ère promesse qui échouera. L'exemple ci-dessous chargera la 2ème url après le succès de chargement de la 1ère.

// On charge la fonction
ajaxGet('https://url_du_site/chemin')
.then(reponse => {
    // La promesse est "tenue"
    console.info('Url chargée !');
    console.log(reponse);
    return ajaxGet('https://url_du_site/chemin2');
})
.then(reponse2 => {
    // La promesse est "tenue"
    console.info('Url2 chargée !');
    console.log(reponse2);
})
.catch(erreur => {
    // Il y a une erreur
    console.error('Erreur !');
    console.dir(erreur);
});

Attention, pour fonctionner, cette méthode impose que la fonction asynchrone retourne une promesse.

Composer les promesses

Le dernier point que nous traiterons sur les promesses (il y en a d'autres), est la composition de promesses.

Dans le cas où vous souhaitez combiner plusieurs fonctions pour traiter toutes leurs réponses en une seule fois, vous pourrez utiliser la méthode "all".

Cette méthode prend un tableau de fonctions comme argument, et génère un tableau de résultats qui peuvent être utilisés ensemble dans la même méthode "then".

L'exemple ci-dessous en montre la syntaxe

Promise.all([fonction1(),fonction3(),fonction3()])
.then(([reponse1, reponse2, reponse3]) => {
    // Ici nous pouvons utiliser reponse1, reponse2 et reponse3
})
.catch(erreur => {
    // Il y a une erreur
    console.error('Erreur !');
    console.dir(erreur);
});

Async et Await

En complément des promesses, nous avons accès à deux mots-clés qui sont "async" et "await".

Notre fonction peut également retourner une promesse si elle est précédée du mot clé "async". Le code ci-dessous retournera donc une promesse.

async function getContenu() {
  return "Contenu différé";
}
 
let resultat = getContenu();
resultat.then(contenu => console.log(contenu));

Dans une fonction appelée avec le mot clé "async", il est possible d'utiliser le mot clé "await" pour demander d'attendre la fin d'un traitement avant de continuer.

async function getContenu() {
  // La ligne ci-dessous arrêtera l'exécution du code tant que le fetch ne sera pas terminé
  let response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  let resultat = response.json();
  console.log(resultat);
}
 
getContenu();

Voilà qui conclut cet article.

Merci de votre visite.

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder aux forums de Nouvelle-Techno.fr ou au serveur Discord pour une entraide par chat

#Tutoriel #Javascript #Live-Coding