Le développement Web moderne s'appuie fortement sur des activités asynchrones pour permettre des applications réactives et interactives. Qu’il s’agisse de récupérer des données depuis une API, de lire des fichiers ou d’exécuter des timers, ces processus doivent s’exécuter en arrière-plan sans figer l’interface. JavaScript vous offre un moyen fiable de gérer ces tâches. Cet article couvre tout ce que vous devez savoir sur les promesses, y compris les idées de base et les fonctionnalités avancées, pour développer des programmes asynchrones sans erreur.
Dans cet article, vous découvrirez —
Qu'est-ce qu'une promesse ?
Pourquoi utiliser les promesses ?
Comment fonctionnent les promesses ?
Gérer les promesses
Enchaîner les promesses
Gestion des erreurs dans les promesses
Fonctionnalités avancées de la promesse
Flux d'exécution JavaScript avec promesses (important)
Conversion des chaînes de promesses en Async/Await
Meilleures pratiques et erreurs courantes
Une promesse en JavaScript équivaut à faire une « promesse » de faire quelque chose dans le futur. Lorsque vous faites une promesse, vous dites : « Je promets de vous donner les résultats plus tard ». Ce résultat pourrait être un succès ou un échec.
En d'autres termes, une promesse est un objet qui reflète le succès (ou l'échec) ultime d'une opération asynchrone et la valeur qui en résulte. Il vous permet de corréler les gestionnaires avec le succès ou l'échec d'une action asynchrone, rendant votre code plus facile à lire et à maintenir.
En JavaScript, par exemple, les opérations fastidieuses, comme la récupération de données à partir d'un serveur, étaient généralement réalisées avec des rappels. Un rappel est simplement une fonction transmise à une autre fonction à exécuter une fois la tâche terminée. Vous pouvez utiliser un rappel, par exemple, pour traiter les données lorsqu'elles arrivent d'un serveur.
Cependant, lorsqu'il y a des opérations complexes, l'utilisation des rappels devient assez compliquée. Ce désordre est connu sous le nom de « l'enfer des rappels », où l'on peut avoir un rappel dans un autre, ce qui rend le code illisible et ingérable.
Exemple de rappel :
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
Comme indiqué ci-dessus, un tel code devient de plus en plus difficile à lire et à maintenir dans des bases de code plus volumineuses en raison de sa structure profondément imbriquée, souvent appelée « l'enfer des rappels ».
Des promesses ont été introduites pour résoudre ce problème, offrant une manière plus propre et plus organisée de gérer les tâches asynchrones en permettant le chaînage de manière plus lisible.
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
Cette approche aplatit la structure et rend le code plus lisible et maintenable.
Les promesses en JavaScript peuvent être dans l'un des trois états suivants :
En attente : Il s'agit de la première étape. La promesse n’a pas encore été tenue.
Réalisée : La promesse s'est terminée avec succès, ce qui signifie qu'elle est résolue et a une valeur.
Rejeté : la promesse n'a pas abouti et elle comporte un message d'erreur.
Syntaxe de base
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
Dans cet exemple, la promesse se résout après 1 seconde avec le message « Promesse résolue ! ». La méthode.then() est utilisée pour gérer la valeur résolue.
La méthode.then() est utilisée pour gérer ce qui se passe lorsqu'une promesse est terminée avec succès. Il enregistre les fonctions (rappels) à exécuter lorsque la promesse est remplie.
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
La méthode.catch() est utilisée pour gérer ce qui se passe lorsqu'une promesse échoue. Il enregistre une fonction (rappel) à exécuter lorsque la promesse est rejetée.
myPromise.then(data => { console.log("Data received:", data); });
La méthode.finally() vous permet d'exécuter du code une fois la promesse faite, qu'elle ait réussi ou non.
myPromise.catch(error => { console.error("Error:", error); });
Le chaînage permet d'effectuer des tâches de manière séquentielle en transmettant le résultat de la précédente. Passez ensuite au next.then(). Cela vous permet de gérer plusieurs tâches asynchrones de manière séquentielle.
Exemple de chaînage :
myPromise.finally(() => { console.log("Cleanup tasks"); });
Cet exemple utilise each.then() pour gérer chaque étape du processus, permettant un flux de données clair. Cela vous permet de voir comment le résultat d'une étape est transféré à la suivante.
Les promesses simplifient la gestion des erreurs en leur permettant de transmettre la chaîne à la méthode.catch() pour la résolution. Cela élimine le besoin de gérer les échecs à chaque phase, gardant votre code plus clair et plus facile à gérer.
Exemple avec propagation d'erreur :
fetch('https://api.example.com/user') .then(response => response.json()) .then(data => { console.log("Processed data:", data); return processData(data); }) .then(finalResult => { console.log("Final result:", finalResult); }) .catch(error => console.error("Error:", error));
Si une étape de la chaîne de promesse échoue, l'erreur sera détectée par le bloc.catch(). Cela facilite la gestion des problèmes et garantit le bon fonctionnement de votre code.
La méthode Promise.all() vous permet d'exécuter plusieurs promesses simultanément et d'attendre qu'elles se terminent toutes. Si toutes les promesses sont tenues, vous recevrez les résultats de chacune. Si une promesse échoue, il détecte l'erreur.
fetchData() .then(processData) .then(saveData) .catch(error => console.error("An error occurred:", error));
Dans cet exemple, si une promesse échoue, l'intégralité de Promise.all() échoue.
La méthode Promise.race() renvoie le résultat de la première promesse qui se termine, qu'elle réussisse ou échoue.
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
Dans cet exemple, quelle que soit la promesse (fetchData1 ou fetchData2) terminée en premier, son résultat sera enregistré dans la console.
La méthode Promise.allSettled() attend que toutes les promesses que vous lui donnez soient dans un état de réussite ou d'échec, puis se termine. Un tableau est ensuite renvoyé contenant les résultats de chaque promesse.
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
Dans cet exemple, Promise.allSettled() attend la fin de fetchData1() et fetchData2(). Il enregistre ensuite le statut et le résultat (ou l'erreur) de chaque promesse. De cette façon, vous pouvez voir ce qui s'est passé avec chaque promesse, qu'elle ait réussi ou échoué.
La méthode Promise.any() attend que la première promesse soit résolue correctement à partir d'une liste de promesses. Dans le cas où au moins une promesse est résolue, la valeur sera renvoyée par la méthode Promise.any(). Si toutes les promesses sont refusées, cette méthode générera une erreur.
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
Dans cet exemple, Promise.any() attend que la première promesse soit résolue avec succès. La procédure renvoie le résultat de la première promesse réussie, dans ce cas promesse2 avec la valeur « Succès A ». Si toutes les promesses sont refusées, le bloc.catch() est exécuté, enregistrant le message d'erreur. Cette stratégie est bénéfique lorsque l'on souhaite recevoir le résultat de la première promesse réussie sans avoir à attendre la suite.
Voici un exemple pour illustrer cela :
myPromise.then(data => { console.log("Data received:", data); });
Dans cet exemple :
console.log(2) s'exécute en premier car il s'agit d'une opération synchrone régulière.
console.log (6) s'exécute ensuite car il est également synchrone.
Le promise's.then() s'exécute avant le rappel setTimeout car les promesses sont des microtâches, qui ont une priorité plus élevée, donc imprime 3.
Enfin, le rappel setTimeout s'exécute, car il s'agit d'une macrotâche et imprime 4.
Alors rappelez-vous toujours que promise’s.then() s’exécute avant le rappel setTimeout en raison de la priorité de la file d’attente des microtâches.
En JavaScript, le code s'exécute dans un ordre spécifique : d'abord le code synchrone, puis les microtâches (comme les promesses) et enfin les macrotâches (likesetTimeout).
Voici un exemple pour expliquer cela :
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
Dans cet exemple, le code synchrone s'exécute en premier, en enregistrant 3, 6, 2, 7 et 8. Une fois le code synchrone terminé, les microtâches (les rappels.then()) sont traitées, en enregistrant 1 et 9. Enfin, les macrotâches (à partir de setTimeout) s'exécutent dans l'ordre de leurs délais, en enregistrant 21 (0 ms) et 13 (10 ms). Cela met en évidence l'ordre d'exécution de JavaScript : code synchrone > microtâches > macrotâches.
Lorsque vous créez une promesse, le premier appel à résoudre ou à rejeter est le seul qui compte. Tous les autres appels sont rejetés.
Voici un exemple pour illustrer cela :
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
Dans cet exemple, la promesse est résolue avec la valeur 1. La deuxième résolution et les appels de rejet sont ignorés car la promesse a déjà été réglée avec la première résolution.
Lorsque vous enchaînez des promesses, each.then() gère une étape du processus.
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
Dans cet exemple, Promise.resolve(1) commence avec une valeur de 1, mais le premier .then(() => 2) renvoie 2 à la place. Le prochain .then(3) est ignoré et la valeur 2 est transmise. Le .then((value) => value * 3) multiplie la valeur par 3, ce qui donne 6. Le .then(Promise.resolve(4)) ne change pas la valeur, et enfin, .then(console. log) logs 6. Cela montre comment les valeurs sont transmises à travers la chaîne, les valeurs non fonctionnelles étant ignorées.
myPromise.then(data => { console.log("Data received:", data); });
Dans cet exemple, nous enchaînons plusieurs méthodes.then(),.catch() et.finally() pour montrer comment les différentes étapes de résolution de promesse sont gérées. Décomposons-le :
finally() ne reçoit pas d'argument :
Le bloc enfin() exécute le code de nettoyage mais ne prend ni ne transmet aucune valeur. Il est utilisé pour garantir que certains codes s’exécutent quel que soit le résultat de la promesse.
Renvoyer une valeur dansfinal() n'affecte pas la promesse :
Si vous renvoyez une valeur dans le bloc enfin(), cela n'affecte pas la chaîne de promesse ni la valeur finale. Il est exécuté après la résolution/rejet de la promesse mais ne modifie pas le résultat.
Lancer une erreur dansfinal() provoque le rejet :
Si vous lancez une erreur ou renvoyez une promesse rejetée dansfinal(), la chaîne de promesses sera rejetée avec l'erreur ou le motif du rejet.
myPromise.catch(error => { console.error("Error:", error); });
OU
myPromise.finally(() => { console.log("Cleanup tasks"); });
Exemple :
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
Async/await est une méthode d'utilisation de promesses qui fait que le code ressemble davantage au code écrit en mode synchrone. Le terme souvent utilisé est « sucre syntaxique » car il donne un chemin plus simple et plus propre pour créer du code asynchrone.
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
Vous pouvez combiner des promesses avec async/await pour une exécution parallèle en utilisant Promise.all().
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
Évitez l'imbrication profonde : Utilisez le chaînage ou l'async/wait pour garder le code plat et lisible.
Toujours gérer les erreurs : Assurez-vous que chaque chaîne de promesses a un.catch() ou un bloc try/catch.
Utilisez judicieusement l'exécution parallèle : Utilisez Promise.all() uniquement lorsque les tâches sont indépendantes mais doivent se terminer ensemble.
Les promesses JavaScript sont l'un des meilleurs moyens de gérer vos opérations chronophages, telles que la récupération de données sur un serveur. Ils vous aident même à écrire un code plus propre et plus facile à maintenir, et la pratique de ce que vous avez appris vous permettra de tirer pleinement parti du codage asynchrone. Une fois que vous aurez acquis une certaine expérience pratique et commencé à gérer les erreurs avec élégance, les promesses deviendront une partie importante de JavaScript.
Merci d'avoir lu ! Si vous trouvez cet article utile, n'hésitez pas à le souligner, à applaudir, à laisser un commentaire ou même à me contacter sur Twitter/X et LinkedIn car il est très apprécié et aide à garder un contenu comme celui-ci gratuit !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!