Programmation asynchrone JavaScript

La programmation asynchrone en JavaScript : Promises, async/await

JavaScript est mono-thread mais peut gérer l'asynchrone
Cela permet de ne pas bloquer l'exécution pendant les opérations longues

Rappel : Synchrone vs Asynchrone

Code synchrone (bloquant)

console.log("1. Début");
console.log("2. Traitement");
console.log("3. Fin");

Code asynchrone (non-bloquant)

console.log("1. Début");

setTimeout(() => {
    console.log("2. Traitement asynchrone (dans 1ms)");
}, 1);

console.log("3. Fin (s'affiche avant le traitement async!)");

Callbacks (ancien style)

Les callbacks étaient la première façon de gérer l'asynchrone
Problème: "Callback Hell" avec l'imbrication

Exemple de callback simple

function operationAsync(callback) {
    setTimeout(() => {
        const resultat = Math.random() > 0.5 ? "Succès" : "Erreur";
        callback(resultat);
    }, 100);
}

operationAsync((resultat) => {
    console.log("Résultat du callback:", resultat);
});

Problème du Callback Hell

// Code difficile à lire et maintenir
function callbackHell() {
    setTimeout(() => {
        console.log("Étape 1");
        setTimeout(() => {
            console.log("Étape 2");
            setTimeout(() => {
                console.log("Étape 3");
                // Et ça continue...
            }, 100);
        }, 100);
    }, 100);
}

callbackHell();

Promises (ES6)

Les Promises résolvent le problème du callback hell
États d'une Promise: pending, fulfilled (resolved), rejected

Création d'une Promise

function creerPromise(succes = true) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (succes) {
                resolve("Opération réussie!");
            } else {
                reject(new Error("Opération échouée!"));
            }
        }, 100);
    });
}

Utilisation avec .then() et .catch()

creerPromise(true)
    .then(resultat => {
        console.log("Succès:", resultat);
        return "Données transformées";
    })
    .then(donnees => {
        console.log("Transformation:", donnees);
    })
    .catch(erreur => {
        console.error("Erreur:", erreur.message);
    })
    .finally(() => {
        console.log("Nettoyage (always executed)");
    });

Gestion des erreurs

creerPromise(false)
    .then(resultat => {
        console.log("Cette ligne ne s'exécutera pas");
    })
    .catch(erreur => {
        console.error("Erreur capturée:", erreur.message);
    });

Promise.all, Promise.race, etc.

Promise.all

// Promise.all: attend que TOUTES les promises se terminent
console.log("--- Promise.all ---");

const promiseA = creerPromise(true);
const promiseB = new Promise(resolve => setTimeout(() => resolve("B"), 150));
const promiseC = new Promise(resolve => setTimeout(() => resolve("C"), 200));

Promise.all([promiseA, promiseB, promiseC])
    .then(resultats => {
        console.log("Tous les résultats:", resultats); // ["Opération réussie!", "B", "C"]
    })
    .catch(erreur => {
        console.error("Une des promises a échoué:", erreur);
    });

Promise.race

// Promise.race: retourne le résultat de la PREMIÈRE promise qui se termine
console.log("--- Promise.race ---");

const promiseLente = new Promise(resolve => setTimeout(() => resolve("Lente"), 300));
const promiseRapide = new Promise(resolve => setTimeout(() => resolve("Rapide"), 100));

Promise.race([promiseLente, promiseRapide])
    .then(resultat => {
        console.log("Premier résultat:", resultat); // "Rapide"
    });

Promise.allSettled

// Promise.allSettled: attend toutes les promises, même en cas d'erreur
console.log("--- Promise.allSettled ---");

const promiseSuccess = Promise.resolve("Succès");
const promiseError = Promise.reject(new Error("Erreur"));

Promise.allSettled([promiseSuccess, promiseError])
    .then(resultats => {
        console.log("Tous les résultats (settled):", resultats);
        // [{ status: "fulfilled", value: "Succès" }, { status: "rejected", reason: Error }]
    });

Async/Await (ES2017)

async/await rend le code asynchrone plus lisible
Une fonction async retourne toujours une Promise

Fonction async basique

async function fonctionAsync() {
    try {
        console.log("Début de la fonction async");

        const resultat1 = await creerPromise(true);
        console.log("Résultat 1:", resultat1);

        const resultat2 = await new Promise(resolve =>
            setTimeout(() => resolve("Deuxième résultat"), 100)
        );
        console.log("Résultat 2:", resultat2);

        return "Fonction terminée";
    } catch (erreur) {
        console.error("Erreur dans fonction async:", erreur.message);
        throw erreur; // Re-lancer l'erreur
    }
}

// Appel d'une fonction async
fonctionAsync()
    .then(resultat => console.log("Retour de la fonction:", resultat))
    .catch(erreur => console.error("Erreur finale:", erreur.message));

Comparaison Promises vs async/await

Avec Promises (chaînage)

function avecPromises() {
    return creerPromise(true)
        .then(resultat1 => {
            console.log("Promise - Résultat 1:", resultat1);
            return creerPromise(true);
        })
        .then(resultat2 => {
            console.log("Promise - Résultat 2:", resultat2);
            return "Terminé avec Promises";
        });
}

Avec async/await (plus lisible)

async function avecAsyncAwait() {
    const resultat1 = await creerPromise(true);
    console.log("Async - Résultat 1:", resultat1);

    const resultat2 = await creerPromise(true);
    console.log("Async - Résultat 2:", resultat2);

    return "Terminé avec async/await";
}