Les promesses en JavaScript: comprendre le concept
Si vous êtes développeur JavaScript, vous avez probablement déjà entendu parler des promesses. Les promesses permettent de représenter une opération asynchrone, elles sont modélisées par l’objet Promise. Dans cet article, on va découvrir les concepts de base des promesses et comment les utiliser. On verra également comment utiliser les promesses avec async/await.
C’est quoi une promesse ?
Une promesse est un objet qui représente une opération asynchrone qui peut être terminée ou en cours d’exécution. Elle permet de gérer les résultats de ces opérations de manière organisée et efficace. Pour comprendre les promesses voici quelques points clé à retenir :
- Les promesses ont trois états possibles : « fulfilled » (résolue), « rejected » (rejetée) et « pending » (en attente)
- Les promesses ont deux méthodes principales : then et catch qui permettent de gérer les résultats et les erreurs de manière organisée
- Il est possible de chaîner les promesses pour gérer des séquences d’opérations asynchrones
- L’object Promise a quelque méthode static qui permettent optimiser votre code comme all qui permet de gérer plusieurs promesses en parallèle ou encore race de gérer la première promesse à être résolue ou rejetée parmi un groupe de promesses
function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Première opération terminée');
}, 2000);
});
}
function doSomethingElse() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Deuxième opération terminée');
}, 1000);
});
}
doSomething()
.then(result => {
console.log(result);
return doSomethingElse();
})
.then(result => {
console.log(result);
})
.catch(error => console.log(error));
Dans cet exemple, on créé deux fonctions doSomething et doSomethingElse qui retournent des promesses. On utilise les méthodes then pour chaîner ces promesses ensemble et gérer les résultats de ces opérations asynchrones de manière organisée. Ici, on devrait avoir un premier console.log à la deuxième seconde qui affichera « Première opération terminée » à la résolution de la promesse de doSomething et une seconde, la promesse de doSomethingElse devrait émettre sa valeur qui entraînera un console.log de « Deuxième opération terminée ».
Organise les opérations asynchrones avec Promise.all et Promise.race
Il existe des méthodes pour gérer plusieurs promesses en parallèle afin de gérer vos opérations asynchrones de manière organisée et efficace. Les méthodes Promise.all et Promise.race vous permettent de maximiser la puissance des promesses en gérant simultanément plusieurs opérations asynchrones.
Fait jouer la concurrence avec Promise.race
Race permet d’exécuter plusieurs promesses en parallèle et de garder la valeur de la première promesse à émettre (résolue ou rejetée) et d’annuler les autres qui sont encore au statut « en attente ». Cette méthode peut être utile si vous voulez mettre en place un timeout à vos appels HTTP au niveau du client par exemple, l’implémentation simple serait de faire un race entre un fetch (qui retourne une promesse) et une promesse avec un setTimeout de 30 secondes qui utilise sa fonction reject au bout des 30 secondes.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promesse 1 terminée après 2 secondes');
}, 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promesse 2 terminée après 3 secondes');
}, 3000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promesse 3 terminée après 1 seconde');
}, 1000);
});
Promise.race([promise1, promise2, promise3])
.then(result => console.log(result))
.catch(error => console.log(error));
Pour cet exemple, on a trois promesses qui retournent une chaîne de caractères au bout d’un certain temps via un setTimeout. On donne en paramètre à notre Promise.race les trois promesses, ainsi la première promesse à émettre une valeur (ici celle avec un timeout de 1000 millisecondes) sera utiliser et les deux autres seront annulés. Le résultat de ce code sera donc un console.log de « Promesse 3 terminée après 1 seconde ».
Parallélise avec Promise.all
La méthode Promise.all vous permet de maximiser la puissance des promesses en gérant plusieurs opérations asynchrones en parallèle. Cela permet de récupérer tous les résultats dès que toutes les promesses sont résolues, et de les traiter en même temps. Si une des promesses est rejetée, la méthode catch sera appelée avec l’erreur renvoyée par la première promesse qui a été rejetée.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promesse 1 terminée après 2 secondes');
}, 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promesse 2 terminée après 3 secondes');
}, 3000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promesse 3 terminée après 1 seconde');
}, 1000);
});
Promise.all([promise1, promise2, promise3])
.then(result => console.log(result))
.catch(error => console.log(error));
Ici, Promise.all va donc exécuter les trois promesses simultanément et va attendre que les trois aient émis leur valeur. Le résultat de se code sera un console.log d’un tableau avec les valeurs de chaque promesse dans ce tableau.
Améliore la lisibilité des promesses avec async/await
Un des problèmes que les promesses peuvent vous amener à rencontrer est le « callback hell » (enfer des callback). Le « callback hell » dans les promesses peut être rencontrer lorsqu’on fait du chaînage de promesses. Pour simplifier la syntaxe des promesses, il est possible d’utiliser async/await.
async function getData() {
try {
const response = await fetch('https://example.com/data');
const data = await response.json();
const { id } = data;
const response = await fetch('https://example.com/data/' + id);
const data2 = await response.json();
console.log(data, data2);
} catch (error) {
console.error(error);
}
}
getData();
Dans cet exemple, on créé une fonction asynchrone getData qui utilise fetch pour récupérer des données à partir d’une URL. La fonction fetch retourne une promesse qui est résolue avec une réponse HTTP. On utilise la syntaxe async/await pour gérer cette promesse, en utilisant await pour attendre que la promesse soit résolue, et en stockant le résultat dans une variable response, et on utilisera ensuite ce résultat pour exécuter une deuxième requête sans avoir à faire d’imbrication vu qu’on utilise await. Nous utilisons également la méthode json pour transformer la réponse en données exploitables. Si une erreur se produit pendant le traitement de la promesse, le bloc catch sera exécuté pour capturer l’erreur.
Il est important de noter que l’utilisation de await doit être faite dans un Execution Context déclaré comme async pour pouvoir être utilisé, pour rappel le scope global est déclaré comme async .
Pour résumer, c’est quoi les promesses ?
- Les promesses sont des objets utilisés pour gérer les opérations asynchrones
- Les promesses ont un état qui peut être « résolu », « rejeté » ou « en attente »
- Le chainage des promesses permet de gérer une séquence d’opérations asynchrones dans un ordre précis
- async/await est une syntaxe qui permet de gérer les promesses de manière plus lisible et simple à comprendre
- Il existe des méthodes pour gérer plusieurs promesses simultanément telles que Promise.all et Promise.race
Proposition de lectures
Immediately Invoked Function Expression (IIFE) sont des fonctions utilisées automatiquement et directement après leur déclaration.
Optimiser les performances de vos pages web avec PageSpeed Module, un outils à brancher directement sur votre serveur web