Maison >interface Web >js tutoriel >JavaScript asynchrone - Le parcours des rappels à l'attente asynchrone

JavaScript asynchrone - Le parcours des rappels à l'attente asynchrone

Barbara Streisand
Barbara Streisandoriginal
2024-11-29 03:41:13899parcourir

En tant que langage monothread, JavaScript s'est toujours appuyé sur une programmation asynchrone pour gérer les tâches fastidieuses sans bloquer l'exécution du code. Au fil des années, les approches de gestion de l'asynchronicité en JavaScript ont considérablement évolué, devenant plus lisibles, plus faciles à gérer et plus faciles à raisonner. Laissez-moi vous faire voyager à travers l'histoire du JavaScript asynchrone, des rappels aux promesses d'asynchronisation/d'attente.

JavaScript synchrone : la naissance

Au début de JavaScript, avant l'adoption généralisée des rappels, la plupart du code JavaScript était écrit de manière synchrone. Le code synchrone signifie que chaque opération est exécutée l’une après l’autre, de manière bloquante. Lorsqu'une opération de longue durée était rencontrée, l'exécution de l'intégralité du script était suspendue jusqu'à ce que cette opération soit terminée.

Imaginez que vous êtes au guichet d'une gare avec un seul vendeur de billets. Vous demandez un billet et le vendeur de billets commence à traiter votre demande. Dans le modèle synchrone, vous devrez attendre au comptoir jusqu'à ce que le vendeur de billets ait fini de traiter votre demande et vous remette le billet. Pendant ce temps, personne d'autre ne peut être servi et l'ensemble de la billetterie est bloqué.

Asynchronous JavaScript - The Journey from Callbacks to Async Await

Voici un exemple de code JavaScript synchrone :

console.log("Before operation"); 

// Simulating a long-running operation 
for (let i = 0; i < 1000000000; i++) { 
// Performing some computation 
} 

console.log("After operation");

Dans ce code, les instructions console.log seront exécutées dans l'ordre et l'opération de longue durée (la boucle for) bloquera l'exécution du script jusqu'à ce qu'il se termine. Le message "Après opération" ne sera enregistré qu'une fois la boucle terminée.

Le problème avec le code synchrone

Bien que le code synchrone soit simple à comprendre et à raisonner, il pose plusieurs problèmes, notamment lorsqu'il s'agit d'opérations chronophages :

  1. Blocage de l'exécution : les opérations de longue durée bloquent l'exécution de l'intégralité du script, rendant la page Web ou l'application insensible jusqu'à la fin de l'opération.
  2. Mauvaise expérience utilisateur : le blocage de l'exécution peut conduire à une mauvaise expérience utilisateur, car les utilisateurs doivent attendre la fin des opérations avant d'interagir avec l'application.
  3. Utilisation inefficace des ressources : le code synchrone ne permet pas une utilisation efficace des ressources système, car il ne peut pas effectuer d'autres tâches en attendant la fin d'une opération.

Pour surmonter les limites du code synchrone et offrir une meilleure expérience utilisateur, des techniques de programmation asynchrone ont été introduites. La programmation asynchrone permet d'exécuter des opérations de longue durée en arrière-plan sans bloquer l'exécution du reste du code et c'est ainsi que le rappel a été introduit.

Rappels : les premiers jours

Les rappels étaient le principal moyen de gérer les opérations asynchrones. Un rappel est simplement une fonction passée en argument à une autre fonction, à exécuter ultérieurement une fois l'opération asynchrone terminée.

Imaginez que vous souhaitiez acheter un billet de train. Vous vous présentez au guichet de la gare et demandez un billet pour une destination précise. Le vendeur de billets prend en compte votre demande et vous demande d'attendre le temps de vérifier la disponibilité des places dans le train. Vous leur fournissez vos coordonnées et attendez dans la salle d’attente. Une fois que le vendeur de billets a traité votre demande et qu'une place est disponible, il vous appelle pour vous informer que votre billet est prêt à être récupéré. Dans cette analogie, vos coordonnées constituent le rappel - un moyen pour le vendeur de billets de vous avertir lorsque la tâche asynchrone (vérifier la disponibilité des sièges et émettre le billet) est terminée.

Asynchronous JavaScript - The Journey from Callbacks to Async Await

Voici comment l'analogie se rapporte aux rappels en JavaScript :

  • Demander un billet de train, c'est comme appeler une fonction asynchrone qui prend un rappel comme argument.
  • Fournir vos coordonnées au vendeur de billets, c'est comme transmettre une fonction de rappel à la fonction asynchrone.
  • Le vendeur de billets vérifiant la disponibilité des sièges et traitant votre demande est comme une opération asynchrone en cours d'exécution.
  • Le vendeur de billets qui appelle votre nom lorsque le billet est prêt est comme la fonction de rappel invoquée avec le résultat de l'opération asynchrone.
  • Vous attendez dans la zone d'attente, c'est comme si le reste de l'exécution de votre code se poursuivait pendant que l'opération asynchrone était en cours de traitement.

Dans l'approche callback, vous fournissez une fonction (le callback) qui sera appelée une fois l'opération asynchrone terminée. La fonction asynchrone effectue sa tâche puis appelle le rappel avec le résultat ou l'erreur, permettant à votre code de gérer le résultat de l'opération asynchrone.

Voici un exemple d'appel API à l'aide de rappels dans Node.js :

console.log("Before operation"); 

// Simulating a long-running operation 
for (let i = 0; i < 1000000000; i++) { 
// Performing some computation 
} 

console.log("After operation");

Dans cet exemple, nous avons une fonction fetchData qui simule un appel API. Il prend un paramètre url et une fonction de rappel comme arguments. À l'intérieur de la fonction, nous utilisons setTimeout pour simuler un délai de 1 000 millisecondes (1 seconde) avant d'appeler la fonction de rappel.

La fonction de rappel suit la convention commune consistant à accepter une erreur comme premier argument (err) et les données comme deuxième argument (data). Dans cet exemple, nous simulons un appel d'API réussi en définissant l'erreur sur null et en fournissant un exemple d'objet de données.

Pour utiliser la fonction fetchData, nous l'appelons avec une URL et une fonction de rappel. Dans la fonction de rappel, nous vérifions d'abord si une erreur s'est produite en vérifiant l'argument err. Si une erreur existe, nous l'enregistrons sur la console à l'aide de console.error et revenons pour arrêter la poursuite de l'exécution.

Si aucune erreur ne s'est produite, nous enregistrons les données reçues sur la console à l'aide de console.log.

Lorsque vous exécutez ce code, il simulera un appel API asynchrone. Après un délai d'1 seconde, la fonction de rappel sera invoquée et le résultat sera enregistré sur la console :

{ identifiant : 1, nom : 'John Doe' }

Cet exemple montre comment les rappels peuvent être utilisés pour gérer les appels d'API asynchrones. La fonction de rappel est passée en argument à la fonction asynchrone (fetchData) et elle est invoquée une fois l'opération asynchrone terminée, soit avec une erreur, soit avec les données résultantes.

Bien que les rappels aient fait le travail, ils présentaient plusieurs inconvénients :

Asynchronous JavaScript - The Journey from Callbacks to Async Await

  1. Callback Hell/Pyramide of Doom : L'imbrication de plusieurs opérations asynchrones a conduit à un code profondément imbriqué et indenté, le rendant difficile à lire et à maintenir.
  2. Gestion des erreurs : La gestion des erreurs à chaque niveau d'imbrication était fastidieuse et conduisait souvent à un code répétitif.
  3. Manque de lisibilité : La nature non linéaire des rappels rendait le code plus difficile à suivre et à raisonner.

Promesses : un pas en avant

Pour relever les défis liés aux rappels, des promesses ont été introduites dans ES6 (ECMAScript 2015). Une promesse représente l'achèvement ou l'échec éventuel d'une opération asynchrone et vous permet d'enchaîner des opérations entre elles.

Pensez à une promesse comme à un billet de train. Lorsque vous achetez un billet de train, celui-ci représente une promesse de la compagnie ferroviaire que vous pourrez monter à bord du train et atteindre votre destination. Le billet contient des informations sur le train, telles que l'heure de départ, l'itinéraire et le numéro de siège. Une fois que vous avez le billet, vous pouvez attendre l'arrivée du train, et lorsqu'il est prêt à embarquer, vous pouvez monter dans le train avec votre billet.

Dans cette analogie, le billet de train est la promesse. Il représente l’aboutissement éventuel d’une opération asynchrone (le voyage en train). Vous conservez le ticket (l'objet de promesse) jusqu'à ce que le train soit prêt (l'opération asynchrone est terminée). Une fois la promesse résolue (le train arrive), vous pouvez utiliser le billet pour monter à bord du train (accéder à la valeur résolue).

Voici comment l'analogie est liée aux promesses en JavaScript :

  • Acheter le billet de train, c'est comme appeler une fonction asynchrone qui renvoie une promesse.
  • Le billet de train lui-même est l'objet de la promesse, représentant le résultat futur de l'opération asynchrone.
  • Attendre l'arrivée du train, c'est comme attendre que la promesse soit résolue ou rejetée.
  • Monter à bord du train avec le billet, c'est comme utiliser .then() pour accéder à la valeur résolue de la promesse.
  • Si le train est annulé (une erreur se produit), c'est comme si la promesse était rejetée, et vous la géreriez avec .catch().

Les promesses fournissent un moyen structuré de gérer les opérations asynchrones, vous permettant d'enchaîner plusieurs opérations et de gérer les erreurs de manière plus gérable, tout comme la façon dont un billet de train vous aide à organiser et à gérer votre voyage en train.

Asynchronous JavaScript - The Journey from Callbacks to Async Await

Voici un exemple d'appel d'API à l'aide de promesses :

console.log("Before operation"); 

// Simulating a long-running operation 
for (let i = 0; i < 1000000000; i++) { 
// Performing some computation 
} 

console.log("After operation");

Dans ce code, la fonction fetchData renvoie une promesse. Le constructeur de promesse prend une fonction qui accepte deux arguments : résoudre et rejeter. Ces fonctions sont utilisées pour contrôler l'état de la promesse.

Dans le constructeur de promesse, nous simulons un appel API en utilisant setTimeout, comme dans l'exemple précédent. Cependant, au lieu d'invoquer une fonction de rappel, nous utilisons les fonctions de résolution et de rejet pour gérer le résultat asynchrone.

Si une erreur se produit (dans cet exemple, nous la simulons en vérifiant la variable d'erreur), nous appelons la fonction de rejet avec l'erreur, indiquant que la promesse doit être rejetée.

Si aucune erreur ne se produit, nous appelons la fonction de résolution avec les données, indiquant que la promesse doit être résolue avec les données reçues.

Pour utiliser la fonction fetchData, nous enchaînons les méthodes .then() et .catch() à l'appel de fonction. La méthode .then() est utilisée pour gérer la valeur résolue de la promesse, tandis que la méthode .catch() est utilisée pour gérer les erreurs qui peuvent survenir.

Si la promesse est résolue avec succès, la méthode .then() est invoquée avec les données résolues et nous les enregistrons dans la console à l'aide de console.log.

Si une erreur se produit et que la promesse est rejetée, la méthode .catch() est invoquée avec l'objet err et nous l'enregistrons sur la console à l'aide de console.error.

L'utilisation de promesses offre un moyen plus structuré et plus lisible de gérer les opérations asynchrones par rapport aux rappels. Les promesses vous permettent d'enchaîner plusieurs opérations asynchrones à l'aide de .then() et de gérer les erreurs de manière plus centralisée à l'aide de .catch().

Promesses améliorées lors des rappels de plusieurs manières :

  1. Chaînage : les promesses vous permettaient d'enchaîner plusieurs opérations asynchrones ensemble à l'aide de .then, rendant le code plus lisible et plus facile à suivre.
  2. Gestion des erreurs : Promises a fourni une méthode .catch pour gérer les erreurs de manière plus centralisée et rationalisée.
  3. Lisibilité : les promesses ont fait ressembler le code asynchrone davantage à du code synchrone, améliorant ainsi la lisibilité.

Cependant, les promesses avaient encore certaines limites. L'enchaînement de plusieurs promesses pouvait toujours conduire à un code profondément imbriqué, et la syntaxe n'était pas aussi claire qu'elle pourrait l'être.

Async/Await : l'approche moderne

Async/await, introduit dans ES8 (ECMAScript 2017), repose sur des promesses et fournit une manière plus synchrone d'écrire du code asynchrone.

Avec async/await, vous pouvez écrire du code asynchrone qui ressemble et se comporte comme du code synchrone. C'est comme si vous aviez un assistant personnel qui se rend au guichet à votre place. Vous attendez simplement le retour de votre assistant avec le billet, et une fois qu'il l'a fait, vous pouvez continuer votre voyage.

Voici un exemple d'appel API à l'aide de async/await :

console.log("Before operation"); 

// Simulating a long-running operation 
for (let i = 0; i < 1000000000; i++) { 
// Performing some computation 
} 

console.log("After operation");

Dans ce code, nous avons une fonction asynchrone appelée fetchData qui prend un paramètre url représentant le point de terminaison de l'API. À l'intérieur de la fonction, nous utilisons un bloc try/catch pour gérer les erreurs pouvant survenir lors de la requête API.

Nous utilisons le mot-clé wait avant la fonction fetch pour suspendre l'exécution jusqu'à ce que la promesse renvoyée par fetch soit résolue. Cela signifie que la fonction attendra que la requête API soit terminée avant de passer à la ligne suivante.

Une fois la réponse reçue, nous utilisons wait réponse.json() pour analyser le corps de la réponse au format JSON. Il s'agit également d'une opération asynchrone, nous utilisons donc wait pour attendre la fin de l'analyse.

Si la requête API et l'analyse JSON réussissent, les données sont renvoyées par la fonction fetchData.

Si une erreur se produit lors de la requête API ou de l'analyse JSON, elle est détectée par le bloc catch. Nous enregistrons l'erreur sur la console à l'aide de console.error et renvoyons l'erreur à l'aide de throw err pour la propager à l'appelant.

Pour utiliser la fonction fetchData, nous avons une fonction asynchrone appelée main. Dans main, nous spécifions l'URL du point de terminaison de l'API à partir duquel nous souhaitons récupérer les données.

Nous utilisons wait fetchData(url) pour appeler la fonction fetchData et attendons qu'elle renvoie les données. Si la requête API réussit, nous enregistrons les données reçues dans la console.

Si une erreur se produit lors de la requête API, elle est détectée par le bloc catch dans la fonction principale. Nous enregistrons l'erreur sur la console en utilisant console.error.

Enfin, nous appelons la fonction principale pour démarrer l'exécution du programme.

Lorsque vous exécutez ce code, il effectuera une requête API asynchrone vers l'URL spécifiée à l'aide de la fonction fetch. Si la demande réussit, les données reçues seront enregistrées sur la console. Si une erreur se produit, elle sera également détectée et enregistrée.

L'utilisation de async/await avec la fonction fetch fournit un moyen propre et lisible de gérer les requêtes API asynchrones. Il vous permet d'écrire du code asynchrone qui ressemble et se comporte comme du code synchrone, ce qui le rend plus facile à comprendre et à maintenir.

Async/await offre plusieurs avantages :

  1. Code concis : Async/await vous permet d'écrire du code asynchrone qui ressemble à du code synchrone, le rendant plus concis et lisible.
  2. Gestion des erreurs : Async/await utilise des blocs try/catch pour la gestion des erreurs, ce qui est une manière plus familière et intuitive de gérer les erreurs par rapport à .catch avec des promesses.
  3. Lisibilité : Async/await rend le code asynchrone plus facile à comprendre et à raisonner, en particulier pour les développeurs familiarisés avec la programmation synchrone.

Conclusion

Asynchronous JavaScript - The Journey from Callbacks to Async Await

En conclusion, l'évolution du JavaScript asynchrone, des rappels aux promesses d'async/wait, a été un voyage vers un code asynchrone plus lisible, gérable et maintenable. Chaque étape s'appuie sur la précédente, corrigeant les limites et améliorant l'expérience du développeur.

Aujourd'hui, async/await est largement utilisé et est devenu le moyen préféré pour gérer les opérations asynchrones en JavaScript. Il permet aux développeurs d'écrire du code asynchrone propre, concis et facile à comprendre, ce qui en fait un outil précieux dans la boîte à outils de chaque développeur JavaScript.

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn