Écrit par Rosario De Chiara✏️
En JavaScript, les promesses sont un outil puissant pour gérer les opérations asynchrones, particulièrement utile dans les événements liés à l'interface utilisateur. Ils représentent une valeur qui n’est peut-être pas disponible immédiatement mais qui sera résolue à un moment donné dans le futur.
Les promesses permettent (ou devraient permettre) aux développeurs d'écrire du code plus propre et plus gérable lorsqu'ils traitent des tâches telles que les appels d'API, les interactions utilisateur ou les animations. En utilisant des méthodes telles que .then(), .catch() et .finally(), Promises permet de gérer de manière plus intuitive les scénarios de réussite et d'erreur, évitant ainsi le fameux « enfer des rappels ».
Dans cet article, nous utiliserons la nouvelle méthode (promise.withResolvers() de mars 2024) qui vous permet d'écrire du code plus propre et plus simple en renvoyant un objet contenant trois éléments : une nouvelle promesse et deux fonctions, une pour résoudre la promesse. et l'autre pour la rejeter Comme il s'agit d'une mise à jour récente, vous aurez besoin d'un runtime Node récent (v>22) pour exécuter les exemples de cet article.
Dans les deux morceaux de code fonctionnellement équivalents suivants, nous pouvons comparer l'ancienne approche et la nouvelle approche consistant à attribuer la méthode pour résoudre ou rejeter une promesse :
let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); Math.random() > 0.5 ? resolve("ok") : reject("not ok");
Dans le code ci-dessus, vous pouvez voir l'utilisation la plus traditionnelle d'une promesse : vous instanciez un nouvel objet de promesse, puis, dans le constructeur, vous devez attribuer les deux fonctions, résoudre et rejeter, qui seront invoquées lorsque nécessaire.
Dans l'extrait de code suivant, le même morceau de code a été réécrit avec la nouvelle méthode Promise.withResolvers(), et il semble plus simple :
const { promise, resolve, reject } = Promise.withResolvers(); Math.random() > 0.5 ? resolve("ok") : reject("not ok");
Vous pouvez voir ici comment fonctionne la nouvelle approche. Il renvoie la promesse, sur laquelle vous pouvez appeler la méthode .then() et les deux fonctions, résoudre et rejeter.
L'approche traditionnelle des promesses encapsule la logique de création et de gestion des événements dans une seule fonction, ce qui peut être limitant si plusieurs conditions ou différentes parties du code doivent résoudre ou rejeter la promesse.
En revanche, Promise.withResolvers() offre une plus grande flexibilité en séparant la création de la promesse de la logique de résolution, ce qui la rend adaptée à la gestion de conditions complexes ou de plusieurs événements. Toutefois, pour les cas d'utilisation simples, la méthode traditionnelle peut être plus simple et plus familière à ceux qui sont habitués aux modèles de promesses standard.
Nous pouvons désormais tester la nouvelle approche sur un exemple plus réaliste. Dans le code ci-dessous, vous pouvez voir un exemple simple d'invocation d'API :
function fetchData(url) { return new Promise((resolve, reject) => { fetch(url) .then(response => { // Check if the response is okay (status 200-299) if (response.ok) { return response.json(); // Parse JSON if response is okay } else { // Reject the promise if the response is not okay reject(new Error('API Invocation failed')); } }) .then(data => { // Resolve the promise with the data resolve(data); }) .catch(error => { // Catch and reject the promise if there is a network error reject(error); }); }); } // Example usage const apiURL = ''; fetchData(apiURL) .then(data => { // Handle the resolved data console.log('Data received:', data); }) .catch(error => { // Handle any errors that occurred console.error('Error occurred:', error); });
La fonction fetchData est conçue pour prendre une URL et renvoyer une promesse qui gère un appel d'API à l'aide de l'API fetch. Il traite la réponse en vérifiant si l'état de la réponse est compris entre 200 et 299, indiquant le succès.
En cas de succès, la réponse est analysée au format JSON et la promesse est résolue avec les données résultantes. Si la réponse échoue, la promesse est rejetée avec un message d'erreur approprié. De plus, la fonction inclut une gestion des erreurs pour détecter toute erreur réseau, rejetant la promesse si une telle erreur se produit.
L'exemple montre comment utiliser cette fonction, montrant comment gérer les données résolues avec un bloc .then() et gérer les erreurs à l'aide d'un bloc .catch(), garantissant que la récupération réussie des données et les erreurs sont gérées de manière appropriée.
Dans le code ci-dessous, nous réécrivons la fonction fetchData() en utilisant la nouvelle méthode Promise.withResolvers() :
function fetchData(url) { const { promise, resolve, reject } = Promise.withResolvers(); fetch(url) .then(response => { // Check if the response is okay (status 200-299) if (response.ok) { return response.json(); // Parse JSON if response is okay } else { // Reject the promise if the response is not okay reject(new Error('API Invocation failed')); } }) .then(data => { // Resolve the promise with the data resolve(data); }) .catch(error => { // Catch and reject the promise if there is a network error reject(error); }); return promise; }
Comme vous pouvez le constater, le code ci-dessus est plus lisible et le rôle de l'objet Promise est clair : la fonction fetchData renverra une promesse qui sera résolue avec succès ou échouera, en invoquant – dans chaque cas – la méthode appropriée. . Vous pouvez trouver le code ci-dessus sur le référentiel nommé api.invocation.{old|new}.js.
Le code suivant explore comment implémenter une méthode d'annulation de promesse. Comme vous le savez peut-être, vous ne pouvez pas annuler une promesse en JavaScript. Les promesses représentent le résultat d'une opération asynchrone et elles sont conçues pour être résolues ou rejetées une fois créées, sans mécanisme intégré pour les annuler.
Cette limitation survient parce que les promesses ont un processus de transition d'état défini ; ils commencent comme en attente et, une fois réglés, ne peuvent pas changer d’état. Ils sont censés encapsuler le résultat d’une opération plutôt que de contrôler l’opération elle-même, ce qui signifie qu’ils ne peuvent pas influencer ou annuler le processus sous-jacent. Ce choix de conception maintient Promises simple et axé sur la représentation du résultat final d'une opération :
const cancellablePromise = () => { const { promise, resolve, reject } = Promise.withResolvers(); promise.cancel = () => { reject("the promise got cancelled"); }; return promise; };
Dans le code ci-dessus, vous pouvez voir l'objet nommé CancellablePromise, qui est une promesse avec une méthode Cancel() supplémentaire qui, comme vous pouvez le voir, force simplement l'invocation de la méthode de rejet. Il ne s'agit que d'un sucre syntaxique et n'annule pas une promesse JavaScript, bien que cela puisse aider à écrire un code plus clair.
Une approche alternative consiste à utiliser un AbortController et un AbortSignal, qui peuvent être liés à l'opération sous-jacente (par exemple, une requête HTTP) pour l'annuler en cas de besoin. Dans la documentation, vous pouvez voir que l'approche AbortController et AbortSignal est une implémentation plus expressive de ce que nous avons implémenté dans le code ci-dessus : une fois AbortSignal invoqué, la promesse est simplement rejetée.
Une autre approche consiste à utiliser des bibliothèques de programmation réactives comme RxJS, qui offrent une implémentation du modèle Observable, un contrôle plus sophistiqué sur les flux de données asynchrones, y compris des capacités d'annulation.
Lorsqu'on parle de cas d'utilisation pratiques, les promesses sont bien adaptées à la gestion d'opérations asynchrones uniques, telles que la récupération de données à partir d'une API. En revanche, les observables sont idéaux pour gérer des flux de données, tels que les entrées utilisateur, les événements WebSocket ou les réponses HTTP, où plusieurs valeurs peuvent être émises au fil du temps.
Nous avons déjà précisé qu'une fois lancées, les promesses ne peuvent pas être annulées, tandis que les observables permettent l'annulation en se désabonnant du flux. L'idée générale est qu'avec les Observables, vous disposez d'une structure explicite de l'interaction possible avec l'objet :
Ceci est démontré dans le code ci-dessous :
import { Observable } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); }); const observer = observable.subscribe({ next(x) { console.log('Received value:', x); }, complete() { console.log('Observable completed'); } }); observer.unsubscribe();
Ce code ne peut pas être réécrit avec des promesses car l'observable renvoie trois valeurs alors qu'une promesse ne peut être résolue qu'une seule fois.
Pour expérimenter davantage la méthode de désabonnement, nous pouvons ajouter un autre Observer qui utilisera la méthode takeWhile() : il permettra à l'Observateur d'attendre que les valeurs correspondent à une condition spécifique ; dans le code ci-dessous, par exemple, il continue de recevoir des événements de l'Observable alors que la valeur n'est pas 2 :
import { Observable, takeWhile } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); }); const observer1 = observable.subscribe({ next(x) { console.log('Received by 1 value:', x); }, complete() { console.log('Observable 1 completed'); } }); const observer2 = observable.pipe( takeWhile(value => value != "2") ).subscribe(value => console.log('Received by 2 value:', value));
Dans le code ci-dessus, observer1 est le même que ce que nous avons déjà vu : il va simplement s'abonner et continuer à recevoir tous les événements de l'Observable. Le second, observer2, recevra des éléments de l'Observable tant que la condition sera remplie. Dans ce cas, cela signifie lorsque la valeur est différente de 2.
Depuis l'exécution, vous pouvez voir comment fonctionnent les deux mécanismes différents :
$ node observable.mjs Received by 1 value: 1 Received by 1 value: 2 Received by 1 value: 3 Observable 1 completed Received by 2 value: 1 $
Dans cet article, nous avons étudié le nouveau mécanisme d'attribution d'une promesse en JavaScript et présenté certaines des façons possibles d'annuler une promesse avant son achèvement. Nous avons également comparé les Promises avec les objets Observables, qui non seulement offrent les fonctionnalités des Promises mais les étendent en permettant de multiples émissions d'événements et un mécanisme de désabonnement approprié.
Le débogage du code est toujours une tâche fastidieuse. Mais plus vous comprenez vos erreurs, plus il est facile de les corriger.
LogRocket vous permet de comprendre ces erreurs de manière nouvelle et unique. Notre solution de surveillance front-end suit l'engagement des utilisateurs avec vos frontends JavaScript pour vous donner la possibilité de voir exactement ce que l'utilisateur a fait qui a conduit à une erreur.
LogRocket enregistre les journaux de la console, les temps de chargement des pages, les traces de pile, les requêtes/réponses réseau lentes avec les corps d'en-tête, les métadonnées du navigateur et les journaux personnalisés. Comprendre l'impact de votre code JavaScript n'aura jamais été aussi simple !
Essayez-le gratuitement.
Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.
Copyright© 2022 湘ICP备2022001581号-3