Comprendre les promesses JavaScript

Bonjour, Habr! Je vous présente la traduction de l'article «Comprendre les promesses en JavaScript» de Sukhjinder Arora.



De l'auteur de la traduction: En plus de l'auteur lui-même, j'espère que l'article vous a été utile. S'il vous plaît, si elle vous a vraiment aidé à apprendre quelque chose de nouveau par vous-même, alors ne soyez pas trop paresseux pour aller à l'article original et remercier l'auteur! Je serai heureux de vos commentaires!

Lien vers la traduction de l'article sur JavaScript asynchrone du même auteur .

JavaScript est un langage de programmation monothread, ce qui signifie qu'une seule chose peut être faite à la fois. Avant ES6, nous utilisions des rappels pour gérer les tâches asynchrones, telles que les requêtes réseau.

En utilisant des promesses, nous pouvons éviter «l'enfer du rappel» et rendre notre code plus propre, plus lisible et plus facile à comprendre.

Supposons que nous voulons obtenir des données du serveur de manière asynchrone, en utilisant des rappels, nous ferions quelque chose comme ceci:

getData(function(x){ console.log(x); getMoreData(x, function(y){ console.log(y); getSomeMoreData(y, function(z){ console.log(z); }); }); }); 

Ici, je demande des données au serveur à l'aide de la fonction getData () , qui reçoit des données à l'intérieur de la fonction de rappel. À l'intérieur de la fonction de rappel, je demande des données supplémentaires en appelant la fonction getMoreData () , en passant les données précédentes en argument, etc.

C'est ce que nous appelons «l'enfer du rappel», où chaque rappel est imbriqué dans l'autre, et chaque rappel interne dépend de son parent.

Nous pouvons réécrire l'extrait ci-dessus en utilisant des promesses:

 getData() .then((x) => { console.log(x); return getMoreData(x); }) .then((y) => { console.log(y); return getSomeMoreData(y); }) .then((z) => { console.log(z); }); 

Vous pouvez voir ce qui est devenu plus lisible qu'avec le premier exemple de rappel.

Quelles sont les promesses?


Une promesse (promesse) est un objet qui contient la valeur future d'une opération asynchrone. Par exemple, si vous demandez des données au serveur, Promis nous promet de recevoir ces données, que nous pourrons utiliser à l'avenir.

Avant de plonger dans tous ces trucs techniques, regardons la terminologie des promesses.

États promis


Une promesse en JavaScript, comme une promesse dans la vie réelle, a 3 états. Cela peut être 1) non résolu (en attente), 2) résolu / résolu (terminé) ou 3) rejeté / rejeté.



Non résolu ou en attente - Promis attend si le résultat n'est pas prêt. Autrement dit, il attend l'achèvement de quelque chose (par exemple, l'achèvement d'une opération asynchrone).
Résolu ou terminé - Promis résolu si le résultat est disponible. Autrement dit, quelque chose a terminé son exécution (par exemple, une opération asynchrone) et tout s'est bien passé.
Rejeté - Promis rejeté si une erreur s'est produite lors de l'exécution.

Maintenant que nous savons ce qu'est Promis et sa terminologie, revenons à la partie pratique des promesses.

Créer des promesses


Dans la plupart des cas, vous utiliserez simplement des promesses, pas les créerez, mais il est toujours important de savoir comment elles sont créées.

Syntaxe:

 const promise = new Promise((resolve, reject) => { ... }); 

Nous avons créé une nouvelle promesse en utilisant le constructeur Promises, cela prend un argument, un rappel, également connu sous le nom de fonction exécutive, qui prend 2 rappels, résoudre et rejeter .

La fonction exécutive est exécutée immédiatement après la création de la promesse. Une promesse est faite en appelant resolver () et rejetée par reject () . Par exemple:

 const promise = new Promise((resolve, reject) => { if(allWentWell) { resolve('  !'); } else { reject('-   '); } }); 

resolver () et rejeter () prennent un argument, qui peut être une chaîne, un nombre, une expression logique, un tableau ou un objet.

Jetons un coup d'œil à un autre exemple pour bien comprendre comment les promesses sont créées.

 const promise = new Promise((resolve, reject) => { const randomNumber = Math.random(); setTimeout(() => { if(randomNumber < .6) { resolve('  !'); } else { reject('-   '); } }, 2000); }); 

Ici, j'ai créé une nouvelle promesse en utilisant le constructeur Promis. Une promesse est exécutée ou rejetée 2 secondes après sa création. Une promesse est exécutée si randomNumber est inférieur à 0,6 et rejeté dans les autres cas.

Lorsqu'une promesse a été créée, elle est en attente et sa valeur n'est pas définie .


Après 2 secondes, le temporisateur se termine, la promesse est exécutée ou rejetée de manière aléatoire, et sa valeur sera celle transmise à la fonction de résolution ou de rejet . Voici un exemple de deux cas:

Achèvement réussi:



Promesse de rejet:



Remarque: la promesse ne peut être exécutée ou rejetée qu'une seule fois. D'autres appels pour résoudre () ou rejeter () n'affecteront en aucun cas l'état de la promesse. Un exemple:

 const promise = new Promise((resolve, reject) => { resolve('Promise resolved'); //   reject('Promise rejected'); //       }); 

Depuis que resolver () a été appelé en premier, la promesse a maintenant le statut «terminée». L'appel ultérieur à rejeter () n'affectera en aucun cas l'état de la promesse.

Utilisation de Promis


Maintenant, nous savons comment créer des promesses, voyons maintenant comment appliquer la promesse déjà créée. Nous utilisons des promesses en utilisant les méthodes then () et catch () .

Par exemple, interroger les données d'une API à l'aide de la récupération , qui renvoie une promesse.

syntaxe .then () : promise.then (successCallback, failureCallback)

successCallback est appelé si la promesse a été exécutée avec succès. Il prend un argument, qui est la valeur transmise à resolver () .

failureCallback est appelé si la promesse a été rejetée. Il prend un argument, qui est la valeur donnée à rejeter () .

Un exemple:

 const promise = new Promise((resolve, reject) => { const randomNumber = Math.random(); if(randomNumber < .7) { resolve('  !'); } else { reject(new Error('-   ')); } }); promise.then((data) => { console.log(data); //  '  !' }, (error) => { console.log(error); //   } ); 

Si la promesse a été exécutée, successCallback est appelée avec la valeur transmise à resolver () . Et si la promesse a été rejetée, failCallback est appelé avec la valeur passée à rejeter ().

Syntaxe .catch () : promise.catch (failureCallback)

Nous utilisons catch () pour gérer les erreurs. Ceci est plus lisible que la gestion des erreurs dans failureCallback dans le rappel de la méthode then () .

 const promise = new Promise((resolve, reject) => { reject(new Error('-   ')); }); promise .then((data) => { console.log(data); }) .catch((error) => { console.log(error); //   }); 

Chaîne de promesse


Les méthodes then () et catch () peuvent également renvoyer une nouvelle promesse, qui peut être traitée par une chaîne d'autres then () à la fin de la méthode then () précédente.

Nous utilisons une chaîne de promesses lorsque nous voulons réaliser une séquence de promesses.

Par exemple:

 const promise1 = new Promise((resolve, reject) => { resolve('Promise1 '); }); const promise2 = new Promise((resolve, reject) => { resolve('Promise2 '); }); const promise3 = new Promise((resolve, reject) => { reject('Promise3 '); }); promise1 .then((data) => { console.log(data); // Promise1  return promise2; }) .then((data) => { console.log(data); // Promise2  return promise3; }) .then((data) => { console.log(data); }) .catch((error) => { console.log(error); // Promise3  }); 

Alors qu'est-ce qui se passe ici?


Lorsque la promesse1 est remplie, la méthode then () est appelée , qui renvoie la promesse2.
Ensuite, lorsque la promesse2 est remplie , () est à nouveau appelé et renvoie la promesse3 .

Puisque promise3 est rejeté, au lieu du suivant alors () , catch () est appelé, qui gère le rejet de promise3 .

Remarque: En règle générale, une méthode catch () suffit pour gérer le rejet de l'une des promesses de la chaîne, si cette méthode est à la fin.

Erreur courante


Beaucoup de nouveaux arrivants font une erreur en investissant des promesses à l'intérieur des autres. Par exemple:

 const promise1 = new Promise((resolve, reject) => { resolve('Promise1 '); }); const promise2 = new Promise((resolve, reject) => { resolve('Promise2 '); }); const promise3 = new Promise((resolve, reject) => { reject('Promise3 '); }); promise1.then((data) => { console.log(data); // Promise1  promise2.then((data) => { console.log(data); // Promise2  promise3.then((data) => { console.log(data); }).catch((error) => { console.log(error); // Promise3  }); }).catch((error) => { console.log(error); }) }).catch((error) => { console.log(error); }); 

Bien que cela fonctionne correctement, il est considéré comme un mauvais style et rend le code moins lisible. Si vous avez une séquence de promesses à exécuter, il vaudra mieux les mettre l'une après l'autre que de les mettre l'une dans l'autre.

Promise.all ()


Cette méthode prend un tableau de promesses et renvoie une nouvelle promesse qui sera exécutée lorsque toutes les promesses à l'intérieur du tableau seront exécutées ou rejetées dès qu'une promesse rejetée sera trouvée. Par exemple:

 const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise1 '); }, 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise2 '); }, 1500); }); Promise.all([promise1, promise2]) .then((data) => console.log(data[0], data[1])) .catch((error) => console.log(error)); 

Ici, l'argument à l'intérieur de then () est un tableau qui contient les valeurs des promesses dans le même ordre dans lequel elles ont été passées à Promise.all () . (Seulement si toutes les promesses sont exécutées)

La promesse est rejetée avec la cause du rejet de la première promesse dans le tableau transféré. Par exemple:

 const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise1 '); }, 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject('Promise2 '); }, 1500); }); Promise.all([promise1, promise2]) .then((data) => console.log(data[0], data[1])) .catch((error) => console.log(error)); // Promise2  

Ici, nous avons deux promesses, où l'une est exécutée après 2 secondes et l'autre s'écarte après 1,5 seconde. Dès que la deuxième promesse est rejetée, la promesse renvoyée par Promise.all () est rejetée sans attendre la première.

Cette méthode peut être utile lorsque vous avez plusieurs promesses et que vous souhaitez savoir quand toutes les promesses sont terminées. Par exemple, si vous demandez des données à une API tierce et que vous souhaitez faire quelque chose avec ces données uniquement lorsque toutes les demandes ont abouti.

Par conséquent, nous avons Promise.all () , qui attend l'exécution réussie de toutes les promesses, ou termine son exécution lorsqu'il détecte le premier échec dans le tableau de promesses.

Promise.race ()


Cette méthode accepte un tableau de promesses et renvoie une nouvelle promesse qui sera exécutée dès que la promesse remplie dans le tableau sera remplie ou rejetée si la promesse rejetée se produit plus tôt. Par exemple:

 const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise1 '); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject('Promise2 '); }, 1500); }); Promise.race([promise1, promise2]) .then((data) => console.log(data)) // Promise1  .catch((error) => console.log(error)); 

Ici, nous avons deux promesses, où l'une est exécutée après 1 seconde et l'autre s'écarte après 1,5 seconde. Dès que la première promesse est remplie, la promesse renvoyée par Promise.race () aura le statut rempli sans attendre le statut de la deuxième promesse.

Ici, les données qui sont passées à then () sont la valeur de la première promesse exécutée.

Par conséquent, Promise.race () attend la première promesse et prend son statut comme le statut de la promesse retournée.

Commentaire de l'auteur de la traduction: D'où le nom lui-même. Race - Race

Conclusion


Nous avons appris quelles sont les promesses et ce qu'elles mangent en JavaScript. Les promesses se composent de deux parties 1) Créez une promesse et 2) Utilisez une promesse. La plupart du temps, vous utiliserez des promesses plutôt que de les créer, mais il est important de savoir comment elles sont créées.

C'est tout, j'espère que cet article vous a été utile!

Source: https://habr.com/ru/post/fr439746/


All Articles