Fermer

mars 22, 2021

Angular Basics Quelles sont les promesses Async / Attendez pourquoi vous vous en souciez


Découvrez comment utiliser les fonctionnalités JavaScript modernes pour gérer les actions asynchrones et comment convertir du code hérité, de bons outils pour démarrer avec Angular.

Le cœur du travail avec Angular, bien sûr, est d'avoir une bonne compréhension de JavaScript. JavaScript est un langage synchrone à thread unique qui exécute le code dans l'ordre défini. Il doit terminer le traitement d'une ligne de code avant de passer à la suivante.

Les navigateurs fournissent une API Web pour initialiser les requêtes asynchrones. Par exemple, si nous voulons envoyer une requête à un serveur, nous pouvons utiliser l'objet XMLHttpRequest ou Fetch API. Une fois qu'une demande asynchrone est terminée, nous devons gérer une réponse réussie ou échouée. Dans le passé, jQuery était largement utilisé pour faire des appels AJAX, je vais donc l'utiliser pour des exemples avec des rappels. Ci-dessous vous pouvez voir le code pour récupérer une liste de personnes à partir de l'API swapi .

 import  $  from   "jquery" ; 

 function   ] onSuccess  ( data  textStatus  jqXHR )   {
  console .  log  ( "Personnes récupérées avec succès!"  data ) ; 
} 

 function   onError  ( jqXHR  textStatus  errorThrown )   {
  console .  error  ( "Un problème est survenu lors de la récupération des données" ) ; 
} 

 function   onComplete  (  jqXHR  textStatus )   {
  console .  log  ( "Request completed" ) ; 
} 

 function   get  ( url  ] onSuccess  onError  onComplete )   {
  $ .  ajax  ( url   {
    méthode :   "GET" 
    success :  onSuccess 
    erreur :  onError 
    terminé :  onComplete
  } ) ; 
} 

 get  ( "https://swapi.co/api/people"  onSuccess  onError  onComplete ) ; 

À l'époque, JavaScript n'avait pas autant de fonctionnalités qu'aujourd'hui, et les rappels étaient utilisés pour gérer les requêtes asynchrones . Malheureusement, l'utilisation de rappels a souvent conduit à un code difficilement maintenable et lisible, en particulier pour les opérations asynchrones plus complexes qui impliquaient de multiples demandes et transformations de données. Vous avez peut-être entendu un terme spécifique souvent associé à cette situation: un enfer de rappel.

Dans l'exemple ci-dessus, nous avons pas mal de code juste pour aller chercher une liste de personnes. Ajoutons un autre appel API et des gestionnaires pour cela et voyons à quel point il est lisible.

 import  $  from   "jquery" ; 

 function   onFetchPlanetsSuccess  ( people )   {
   return   function  ( data  textStatus  jqXHR ) [19659010] {
    console .  log  ( "We got planets and people!"  people  data ) ; [19659072]} ; 
} 

 function   onFetchPlanetsError  ( jqXHR  textStatus )   {
  console .  error  ( "Un problème est survenu lors de la récupération des planètes" ) ; 
} 

 function   onSuccess  ( données  textStatus  jqXHR )   {
  console .  log  ( "Personnes récupérées avec succès!"  data ) ; 
   get  (
     " https://swapi.co/api/planets"[19659006letter,[19659098 OftenFetchPlanetsSuccess[19659006letter([19459003[19659006).
    onFetchPlanetsError
  ) ; 
} 

 function   onError  ( jqXHR  textStatus  errorThrown ) [19659010] {
  console .  error  ( "Un problème est survenu lors de la récupération de personnes" ) ; 
} 

 function   onComplete  ( jqXHR  textStatus )   {
  console .  log  ( "Request completed" ) ; 
} 

 function   get  ( url  ] onSuccess  onError  onComplete )   {
  $ .  ajax  ( url   {
    méthode :   "GET" 
    success :  onSuccess 
    erreur :  onError 
    terminé :  onComplete
  } ) ; 
} 

 get  ( "https://swapi.co/api/people"  onSuccess  onError  onComplete ) ; 

Plus il y a d'appels à faire, plus il devient laid et gênant de maintenir notre code. Il est également un peu plus difficile de suivre le flux d'exécution. Heureusement, ces jours sont derrière nous, car maintenant les actions asynchrones peuvent être gérées avec Promises et Async / Await.

Voyons d'abord ce que sont Promises .

Promises

Des promesses ont été ajoutées à JavaScript dans ES6, également connu sous le nom d'ECMAScript 2015. La raison en était de simplifier la gestion des requêtes asynchrones. Promise est un proxy pour une valeur qui n'est pas encore connue au moment de la création de la promesse. Une promesse peut être dans trois états différents:

  • En attente
  • Accompli
  • Rejeté

Voyons comment les promesses peuvent être utilisées:

 function   get  ( url  ])   {
 
   retour   nouveau   Promesse  ( ( résoudre  rejeter )   =>   {
   
    $ .  ajax  ( url   {
      méthode :   "GET" 
      success :   function  ( data  textStatus  jqXHR )   {
        
        
         resolution  ( données ) ; 
      } 
      erreur :   fonction  ( jqXHR  textStatus  errorThrown )   {
        
        
         rejeter  ( errorThrown ) ; 
      } 
    } ) ; 
  } ) ; 
} 

 get  ( "https://swapi.co/api/people" ) 
  .  puis  ( response  =>   {
    console .  log  ( "response"  response ) ; 
  } ) 
  .  catch  ( error  =>   {
    console .  log  ( "Un problème est survenu lors de l'extraction des données." ) ; 
    console .  error  ( error ) ; 
  } ) 
  .  enfin  ( ([19659006])   =>   {
    console .  log  ( 'request completed' ) 
  } )  ``  `

La méthode get renvoie désormais une instance de l'objet Promise. Une promesse s'attend à recevoir une fonction en tant que paramètre, et elle passera résoudre et rejeter fonctions comme paramètres. Lorsqu'une promesse est initialisée, elle est dans l'état pending . La fonction résoudre est appelée si une demande est exécutée avec succès et changerait l’état de la promesse en rempli . S'il y a un problème lors d'une requête, la fonction rejet est appelée et l'état de la promesse passe à rejeté .

Pour obtenir une réponse de l'appel API lorsqu'il réussit , on peut enchaîner la méthode puis ; il recevra la réponse comme premier paramètre. Si une requête échoue, nous enchaînons la méthode catch . Une autre méthode qui peut être chaînée est enfin .

Vous trouverez ci-dessous un exemple avec l'API Fetch. Nous n'avons pas besoin d'utiliser new Promise ((résoudre, rejeter) => {}) car la méthode fetch renvoie par défaut une promesse.

 fetch  ([19659013] "https://swapi.co/api/people" ) 
  .  puis  ( response  =>   {
     return  réponse .  json  () ; 
  } ) 
  .  then  ( people  = >   {
     return   fetch  ( 'https :  /  /  swapi .  co  /  api  /  planets ') 
 	.  puis  ( response  =>  response .  json  () ) 
	.  puis  ( planets  =>   {
   return   {
    personnes 
    planètes
  } 
} ) 
  } ) 
  .  puis  ( ( { people  ] planètes } )   =>   {
    console .  log  ( 'result'  people  planets ) 
  } )  
   .  catch  ( error  =>   {
    console .  log  ( "Un problème est survenu lors de l'extraction des données." ) ; 
    console .  error  ( error ) ; 
  } ) 
  .  enfin  ( ([19659006])   =>   {
    console .  log  ( 'request completed' ) 
  } ) 

Maintenant nous avons moins de code, il est plus facile à suivre et plus propre que le exemple avec des rappels. Cependant, soyez prudent avec les promesses, car elles peuvent aussi rapidement devenir un gâchis impossible à tenir, surtout s'il y a beaucoup de promesses imbriquées. Par conséquent, essayez de les garder aussi peu profonds que possible et ne les imbriquez pas trop profondément.

Nous avons couvert les bases des promesses, alors voyons maintenant en quoi consiste Async / Await et comment il peut être utilisé pour améliorer notre gestion du code asynchrone.

Async / Await

Dans ECMAScript 2017, une nouvelle fonctionnalité pour gérer les requêtes asynchrones a été introduite: les fonctions async et le mot clé await. Async / Await fonctionne au-dessus des promesses et facilite la lecture et l'écriture du code asynchrone. Le code semble plus synchrone et, par conséquent, le flux et la logique sont plus compréhensibles. Surtout quand cela devient plus complexe et implique plus d'appels et de transformations.

Voici comment définir une fonction asynchrone:


 async   function   fetchData  ()   {[19659324]} 

 const  fetchData  =   async   ()   =>   {
  
} 

La grande différence est juste un ajout du mot-clé async . Cependant, grâce à elle, nous pouvons maintenant attendre des promesses. Vous trouverez ci-dessous l'exemple précédent, mais maintenant réécrit avec async / await.

 async   function   fetchData  ()   {
   try   {[19659241] const  peopleResponse  =   wait   fetch  ( "https://swapi.co/api/people" ) ; 
     const  people  =   wait  peopleResponse .  json  () ; 
     const  planetsResponse  = [19659038] attendre   chercher  ( "https://swapi.co/api/planets" ) ; 
     const  planètes  =   attendre  planetsResponse .  json  () ; 
    console .  log  ( "data"  people  planets ) ; 
  }   catch   ( error )   {
    console .  log  ( "Un problème est survenu lors de l'extraction des données." ) ; 
    console .  error  ( error ) ; 
  }   enfin   {
    console .  log  ( "Request completed" ) ; 
  } 
} 
 fetchData  ()) [19659006]; 

Il n'est pas nécessaire de chaîner des méthodes, car lorsque le moteur JavaScript atteint le mot-clé wait il ne passera pas à la ligne de code suivante tant que la promesse que nous attendons ne sera pas résolue. Nous n'utilisons plus le chaînage puis et catch et, par conséquent, pour gérer les erreurs, nous devons utiliser try / catch.

Nous avons réussi à réduire la quantité de code requise pour récupérer énormément de données. Le code est beaucoup plus facile à maintenir et semble plus synchrone donc il est plus facile de raisonner.

Top-level Await

Le mot-clé await ne peut être utilisé qu'à l'intérieur d'un async fonction. Sinon, une erreur sera générée. Cependant, au moment de la rédaction de cet article, il existe une proposition d'attente de premier niveau qui est actuellement à l'étape 3. Elle permettrait d'utiliser await en dehors d'une asynchrone. fonction. Vous pouvez en savoir plus ici: https://github.com/tc39/proposal-top-level-await .

Async / Await + Promise.all ()

Notre exemple précédent avec async / await est bien meilleur que les tentatives précédentes avec des rappels et des promesses, mais il y a une amélioration que nous pouvons faire. Nous effectuons deux appels API: un pour récupérer des personnes et un pour récupérer des planètes. Cependant, avant que le dernier appel d'API puisse être effectué, le premier doit se terminer en premier. Cela est dû au fonctionnement d’async / await et c’est une perte de temps si le deuxième appel d’API ne repose en aucune façon sur le premier.

Par conséquent, faisons exécuter les deux appels en parallèle. Nous pouvons utiliser Promise.all pour cela.

 async   function   fetchData  ()   {
   try   {
     const  fetchPeoplePromise  =   fetch  ( "https://swapi.co/api/people" ) .  puis  ( response  =>  response .  json  () ) ; 
     const  fetchPlanetsPromise  = [19659008] fetch  ( "https://swapi.co/api/planets" ) .  puis  ( response  =>  réponse .  json  () ) ; 
     const   [ people  planets ] [19659157] =   attendre  Promise .  all  ( [ fetchPeoplePromise  fetchPlanetsPromise ] ) 
    console .  log  ( "data"  people  planets ) ; 
  }   catch   ( error )   {
    console .  log  ( "Un problème est survenu lors de l'extraction des données." ) ; 
    console .  error  ( error ) ; 
  }   enfin   {
    console .  log  ( "Request completed" ) ; 
  } 
} 

Les deux requêtes sont initialisées dès que possible. Comme nous n'avons utilisé le mot clé await sur aucune des requêtes d'extraction, le moteur JavaScript continuera à exécuter le code jusqu'à ce qu'il atteigne la ligne await Promise.all . Promise.all attendra que toutes les promesses passées dans un tableau soient remplies. Si l'une des promesses est rejetée, alors une erreur sera lancée, et elle sera traitée dans le bloc catch .

Personnellement, j'utilise async / await sur des promesses pures chaque fois que je le peux. Cependant, écrire try / catch tout le temps peut être assez fastidieux. Voici donc un petit extrait de code qui peut être utilisé pour vous aider:

 const  withAsync  =   async  fn  =>   {
   try [19659010] {
     const  response  =   wait   fn  () 
     return   [ response   null ] 
  }   catch   ( error )   {
     return   [ null  error ] 
  } 
} 

 const   [ people  error ]   =   wait   withAsync  ( ()   =>   fetch  ( "https://swapi.co/api/people" ) .  puis  ( response  =>  response .  json  () ) 
 if   ( error )   {
  console .  error  ( error ) 
   return 
} 
console .  log  ( 'we have people!'  people ) 

Il n'est pas nécessaire d'écrire try / catch tout le temps. Au lieu de cela, il est encapsulé dans la fonction withAsync . S'il y a une erreur, nous pouvons la gérer et renflouer, et si tout va bien, nous pouvons gérer la réponse.

Conclusion

Nous avons expliqué comment les actions asynchrones en JavaScript peuvent être gérées avec des rappels, des promesses et une asynchrone. /attendre. Ce sont des fonctionnalités clés pour JavaScript et Angular. Les exemples de code montrent clairement à quel point il était difficile dans le passé de gérer les appels d'API. Au moins maintenant, si vous devez travailler avec un ancien projet, vous savez peut-être par où commencer et comment convertir un code plus ancien pour utiliser une approche plus moderne.




Source link