La programmation asynchrone en JavaScript permet d'exécuter des opérations gourmandes en ressources en arrière-plan sans interrompre le thread principal. Les opérations telles que les appels API et les processus de fichiers font partie des opérations qui doivent être exécutées de manière asynchrone.
Promesses. all() est une fonction puissante qui permet de gérer ces opérations simultanément. Cet article expliquera comment gérer plusieurs promesses simultanément avec Promise.all()
Plongeons-nous.
Une promesse est un objet qui représente l'échec ou l'achèvement éventuel d'un événement asynchrone. Regardons une promesse simple.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); });
Une promesse prend une fonction avec deux paramètres, résoudre et rejeter. Dans notre exemple, la promesse sera résolue si l'opération réussit (c'est-à-dire si l'ID utilisateur === 1. Si l'opération échoue, la promesse sera rejetée.
Le cycle de vie d'une promesse commence à l'état en attente, et elle finira par être remplie ou rejetée. Actuellement, la promesse est en attente. Pour consommer la promesse, nous appelons .then() pour gérer le résultat.
Le résultat sera soit les données utilisateur (si remplies), soit une erreur (si rejetée).
promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
Puisque l'opération est réussie, la promesse sera résolue.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); }); promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
si nous modifions la valeur de userId, la promesse sera rejetée et vous obtiendrez l'erreur Utilisateur introuvable
Supposons que vous ayez plusieurs promesses, vous géreriez chaque promesse indépendamment comme ceci :
const promise1 = new Promise((resolve, reject) => resolve(1)); const promise2 = new Promise((resolve, reject) => resolve(2)); const promise3 = new Promise((resolve, reject) => resolve(3)); promise1 .then((value) => { console.log(value); promise2 .then((value) => { console.log(value); promise3 .then((value) => { console.log(value); }) .catch((err) => { console.log("promise3 error", err); }); }) .catch((err) => { console.log("promise2 error", err); }); }) .catch((err) => { console.log("promise1 error", err); });
Il y a quelques problèmes potentiels qui découleront de l'exécution ci-dessus :
Chaque promesse s'exécute une fois la précédente terminée. promise2 démarrera après la résolution de promise1 et promise3 démarrera après la résolution de promise2 ; Cela ralentit l'exécution.
La structure imbriquée dans le chaînage .then entraîne un « enfer de rappel », rendant le code plus difficile à lire et à maintenir.
Chaque erreur est traitée indépendamment, ce qui ajoute à la complexité.
Une meilleure approche serait d'utiliser Promise.all(), qui permet aux promesses de s'exécuter en même temps, améliorant ainsi les performances et la gestion des erreurs
Promise.all() prend un itérable de promesses et renvoie une seule promesse. La syntaxe ressemble à ceci :
Promise.all(iterable)
Si nous utilisons Promise.all() Dans notre exemple précédent, nous avons quelque chose comme ceci :
Promise.all([promise1, promise2, promise3]) .then((values) => { console.log(values); }) .catch((err) => { console.log("promise all error", err); });
Comme vous pouvez le constater, cette approche est plus claire et plus facile à comprendre.
JavaScript est un langage monothread, ce qui signifie que chaque morceau de code attend que le précédent soit terminé avant de passer au suivant.
Donc, si JavaScript est monothread, comment Promise.all() gère-t-il plusieurs promesses ?
Promise.all() fonctionne selon le principe de concurrence, ce qui signifie que toutes les promesses ne commenceront pas nécessairement à s'exécuter au même moment, mais seront initiées sans attendre qu'une promesse soit terminée avant de commencer la suivante.
Promise.all() ne se résout que lorsque toutes les promesses de l'itérable sont remplies. Cependant, si l'une des promesses de l'itérable est rejetée, Promise.all() la rejettera immédiatement et ignorera le résultat des promesses restantes.
Promise.all() excelle dans les scénarios où vous devez effectuer plusieurs opérations asynchrones indépendantes et attendre qu'elles soient toutes terminées avant de continuer.
Examinons quelques-uns de ces exemples dans lesquels Promise.all() peut être utilisé pour améliorer l'efficacité des applications du monde réel.
Considérez un scénario dans lequel vous travaillez sur une application qui récupère simultanément des données de deux API différentes.
Essayons de récupérer les données de manière séquentielle à partir de plusieurs API et enregistrons également le temps nécessaire pour terminer la demande.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); });
Voici le résultat :
Le temps de traitement de la demande est de 50,36 ms. Ce temps d'exécution peut être amélioré. Pour illustrer les avantages de la concurrence, comparons l'approche utilisant Promise.all()
promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
Ici, nous utilisons Promise.all() pour exécuter plusieurs opérations asynchrones simultanément. Promise.all() prendra un tableau de promesses et renverra une seule promesse lorsque toutes les promesses seront résolues.
Voici le résultat.
D'après le résultat, nous pouvons voir que l'utilisation de Promise.all() est légèrement plus efficace : cette amélioration se produit car Promise.all() permet aux deux opérations de démarrer simultanément, plutôt que d'attendre que l'une se termine avant de démarrer l'autre.
Dans les applications du monde réel avec des opérations plus complexes ou des appels d'API supplémentaires, les gains de performances liés à l'utilisation de Promise.all() peuvent être encore plus significatifs.
Cependant, si vous souhaitez attendre que toutes les promesses soient réglées, qu'elles soient remplies ou rejetées, vous pouvez utiliser Promise.allSettled()
Dans ce cas, toutes les données doivent être envoyées en même temps. Dans ce cas, vous pouvez utiliser Promise.all() et envoyer toutes les requêtes simultanément, puis attendre qu'elles soient toutes résolues avant d'obtenir les résultats.
Par exemple, supposons que nous devions analyser cet exemple de données :
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); });
Dans ce cas, toutes les données doivent être envoyées en même temps ; l’envoi séquentiel de données prendra beaucoup de temps. Au lieu de cela, nous utiliserons Promise.all() pour lancer plusieurs appels API simultanément.
Vous aurez quelque chose comme ceci :
promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
Supposons que vous disposiez d'une application qui accepte les téléchargements groupés des utilisateurs. Après avoir pris toutes les mesures nécessaires pour valider les fichiers, vous pouvez utiliser Promise.all() pour effectuer plusieurs lectures de fichiers en parallèle. C'est bien plus efficace que de lire chaque fichier un par un de manière séquentielle.
Sans Promise.all(), vous devrez attendre que chaque fichier soit lu complètement avant de lire le fichier suivant. Cela entraînerait un temps de traitement plus long, surtout si vous avez un plus grand nombre de fichiers.
Cependant, avec Promise.all(), toutes les opérations de lecture de fichiers sont lancées simultanément, ce qui entraîne un gain de temps considérable et une excellente expérience utilisateur.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); }); promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
Il est également important de noter que lorsque vous lisez simultanément de nombreux fichiers volumineux, vous devez être conscient des considérations potentielles en matière de mémoire.
En conclusion, Promise.all() offre de nombreux avantages qui sont résumés ci-dessous
Cleaner Code : Promise.all() rend votre code plus facile à comprendre puisque vous n'avez pas de chaînes .then() imbriquées. Les requêtes sont traitées dans un seul bloc .then().
Efficace : En effectuant des requêtes simultanément, les performances globales de votre application s'améliorent, car le temps total requis pour récupérer les données est réduit.
Recevez des conseils JavaScript pratiques et des extraits de code dans votre boîte de réception. Rejoignez 1 000 développeurs qui écrivent un meilleur code.
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!