Maison >interface Web >js tutoriel >JavaScript asynchrone - Le parcours des rappels à l'attente asynchrone
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.
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é.
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.
Bien que le code synchrone soit simple à comprendre et à raisonner, il pose plusieurs problèmes, notamment lorsqu'il s'agit d'opérations chronophages :
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.
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.
Voici comment l'analogie se rapporte aux rappels en JavaScript :
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 :
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 :
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.
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 :
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, 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.
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!