Article pour bien comprendre les promesses.
L'objet Promise , apparu avec ES2015; est utilisé pour réaliser des traitements de façon asynchrone. Une promesse représente une valeur qui peut être disponible maintenant, dans le futur voire jamais !
Une promesse a 3 états :
- pending (en cours)
- resolve (résolue)
- reject (rejetée)
Ancienne méthode : via des callback
Pour ce premier exemple, j'ai choisi de ne pas utiliser de fonction asynchrone (via setTimeout par exemple) mais une fonction qui retourne un résultat aléatoire dans le but de simplifier le code et donc l'explication.
function getToken(s, f) { if (Math.random() > 0.5) { s("XCOE4dod340CEESee7"); } else f(new Error("Pas plus de token que de beurre à la roulante")); } const success = function(msg) { console.log(msg); }; const failure = function(err) { console.error(err); }; getToken(success, failure);
Avec les promises
La principale différence réside dans le fait que le résultat, une réussite ou un échec est renvoyé respectivement à la méthode "then" ou à la méthode "catch" :
getToken = () => { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { const token = "qsdfEDLSoie5d8899;dEDd" console.log('Token ok'); resolve(token);// renvoie le résultat à la méthode "then()" } else reject(new Error("Pas de chance, vous n'avez pas pu obtenir de token"));// renvoie le résultat à la méthode "catch" }, 2000) }) } getToken() .then(value => { console.log(value); }) .catch(error => { console.error("Erreur: ", error.message); });
Le chaînage de promesses
On peut avoir besoin d'enchaîner les appels de fonction suivant le résultat d'une opération incertaine. Nous simulons ci dessous un processus dans lequel il faut d'abord obtenir un token avant de pouvoir obtenir des infos sur un utilisateur. Il faudra que la méthode "then" renvoie une autre promesse afin de pouvoir les chaîner.
getToken = () => { return new Promise((res, rej) => { setTimeout(() => { if (Math.random() > 0.5) { const token = "qsdfEDLSoie5d8899;dEDd"; console.log("Token ok"); res(token); } else rej(new Error("Pas de chance, vous n'avez pas pu obtenir de token")); }, 2000); }); }; getUser = token => { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { console.log("User ok", token); resolve({ id: 1, token: token }); } else reject(new Error("Pas d'utilisateur")); }, 2000); }); }; getToken() .then(value => { console.log("value dans le premier then : ", value); // notez ici que "then" doit renvoyer une promesse pour que l'on puisse "chaîner" return getUser(value); }) .then(value => { console.log("value dans le deuxième then : ", value); }) .catch(error => { console.error("Erreur: ", error.message); });
Async et await
La déclaration async function et le mot clé await sont des « sucres syntaxiques » apparus avec ES2017. Ils permettent de retrouver une syntaxe plus classique et donc plus lisibles.
async
Le mot clé async devant une déclaration de fonction la transforme en fonction asynchrone. Elle va retourner une promesse. Si la fonction retourne une valeur qui n’est pas une promesse, elle sera automatiquement comprise dans une promesse.
La promesse sera résolue avec la valeur renvoyée par la fonction asynchrone ou sera rompue s’il y a une exception non interceptée émise depuis la fonction asynchrone.
await
Le mot clé await est valable uniquement au sein de fonctions asynchrones définies avec async.
await interrompt l’exécution d’une fonction asynchrone tant qu’une promesse n’est pas résolue ou rejetée.
Exemple de code
Exemple 1 : Réutilisons les fonctions getToken et getUser préalablement définies avec async et await :
async function getTokenUser() { try { const token = await getToken(); // bloque l'exécution jusqu'à obtention de la réponse de la promesse const user = await getUser(token); console.log('Token et user : ', token, user); } catch (error) { console.log('Erreur attrapée : ', error); } } getTokenUser();
Exemple 2 : même fonction en utilisant async et await puis then()
// Promesse en utilisant async et await async function getUniversities() { try { const response = await fetch("http://universities.hipolabs.com/search?country=Italy"); const universities = await response.json(); console.log('universités : ', universities); } catch (error) { console.error('Erreur attrapée : ', error); } } // Promesse en utilisant then function thenGetUniversities() { fetch("http://universities.hipolabs.com/search?country=Italy") .then(response => { return response.json(); }) .then(universities => { console.log('universités : ', universities); }) .catch(error => { console.error('Erreur attrapée : ', error) }) }