L'une des clés pour écrire une application Web réussie est de pouvoir effectuer des dizaines d'appels AJAX par page.
Il s'agit d'un défi de programmation asynchrone typique, et la façon dont vous choisissez de gérer les appels asynchrones fera, en grande partie, la réussite ou l'échec de votre application et, par extension, potentiellement de l'ensemble de votre démarrage.
La synchronisation des tâches asynchrones en JavaScript a été un problème sérieux pendant très longtemps.
Ce défi affecte le back-end développeurs utilisant Node.js autant que les développeurs frontaux utilisant n'importe quel framework JavaScript. La programmation asynchrone fait partie de notre travail quotidien, mais le défi est souvent pris à la légère et non envisagé au bon moment.
La première et la plus simple solution est venue sous la forme de fonctions imbriquées comme rappels . Cette solution a conduit à quelque chose appelé rappel de l'enfer , et trop d'applications en ressentent encore la brûlure.
Ensuite, nous avons Promesses . Ce modèle a rendu le code beaucoup plus facile à lire, mais il était loin du principe de Don’T Repeat Yourself (DRY). Il y avait encore trop de cas où vous deviez répéter les mêmes morceaux de code pour gérer correctement le flux de l'application. Le dernier ajout, sous la forme d'instructions async / await, a finalement rendu le code asynchrone en JavaScript aussi facile à lire et à écrire que n'importe quel autre morceau de code.
Jetons un coup d'œil aux exemples de chacune de ces solutions et réfléchissons à l'évolution de la programmation asynchrone en JavaScript.
Pour ce faire, nous allons examiner une tâche simple qui effectue les étapes suivantes:
comment la Grèce s'est-elle endettée
L'ancienne solution pour synchroniser ces appels était via des rappels imbriqués. Il s’agissait d’une approche décente pour les tâches JavaScript asynchrones simples, mais elle n’était pas adaptée en raison d’un problème appelé rappel de l'enfer .
Le code des trois tâches simples ressemblerait à ceci:
const verifyUser = function(username, password, callback){ dataBase.verifyUser(username, password, (error, userInfo) => { if (error) { callback(error) }else{ dataBase.getRoles(username, (error, roles) => { if (error){ callback(error) }else { dataBase.logAccess(username, (error) => { if (error){ callback(error); }else{ callback(null, userInfo, roles); } }) } }) } }) };
Chaque fonction obtient un argument qui est une autre fonction appelée avec un paramètre qui est la réponse de l'action précédente.
Trop de gens connaîtront un gel du cerveau simplement en lisant la phrase ci-dessus. Avoir une application avec des centaines de blocs de code similaires causera encore plus de problèmes à la personne qui gère le code, même si elle l'a écrite elle-même.
Cet exemple devient encore plus compliqué une fois que vous vous rendez compte qu'un database.getRoles
est une autre fonction qui a des rappels imbriqués.
const getRoles = function (username, callback){ database.connect((connection) => { connection.query('get roles sql', (result) => { callback(null, result); }) }); };
En plus d'avoir un code difficile à maintenir, le principe DRY n'a absolument aucune valeur dans ce cas. La gestion des erreurs, par exemple, est répétée dans chaque fonction et le rappel principal est appelé à partir de chaque fonction imbriquée.
Des opérations JavaScript asynchrones plus complexes, telles que la boucle via des appels asynchrones, représentent un défi encore plus grand. En fait, il n'y a pas de moyen simple de faire cela avec des rappels. C'est pourquoi les bibliothèques JavaScript Promise comme Oiseau bleu et Q eu tellement de traction. Ils permettent d’effectuer des opérations courantes sur des requêtes asynchrones que le langage lui-même ne fournit pas déjà.
C’est là que les promesses JavaScript natives entrent en jeu.
Promesses étaient la prochaine étape logique pour échapper à l'enfer des rappels. Cette méthode n'a pas supprimé l'utilisation des rappels, mais elle a rendu le chaînage des fonctions simple et simplifié le code , ce qui le rend beaucoup plus facile à lire.
Avec Promises en place, le code de notre exemple JavaScript asynchrone ressemblerait à ceci:
const verifyUser = function(username, password) { database.verifyUser(username, password) .then(userInfo => dataBase.getRoles(userInfo)) .then(rolesInfo => dataBase.logAccess(rolesInfo)) .then(finalResult => { //do whatever the 'callback' would do }) .catch((err) => { //do whatever the error handler needs }); };
Pour atteindre ce genre de simplicité, toutes les fonctions utilisées dans l'exemple devraient être Promis . Voyons comment le getRoles
méthode serait mise à jour pour renvoyer un Promise
:
const getRoles = function (username){ return new Promise((resolve, reject) => { database.connect((connection) => { connection.query('get roles sql', (result) => { resolve(result); }) }); }); };
Nous avons modifié la méthode pour renvoyer un Promise
, avec deux rappels, et le Promise
effectue lui-même des actions à partir de la méthode. Maintenant, resolve
et reject
les rappels seront mappés vers Promise.then
et Promise.catch
méthodes respectivement.
Vous remarquerez peut-être que le getRoles
La méthode est toujours sujette à la pyramide du phénomène de catastrophe. Cela est dû à la façon dont les méthodes de base de données sont créées car elles ne renvoient pas Promise
. Si nos méthodes d'accès à la base de données renvoyaient également Promise
le getRoles
La méthode ressemblerait à ceci:
const getRoles = new function (userInfo) { return new Promise((resolve, reject) => { database.connect() .then((connection) => connection.query('get roles sql')) .then((result) => resolve(result)) .catch(reject) }); };
La pyramide de malheur a été considérablement atténuée avec l'introduction des promesses. Cependant, nous devions toujours nous fier aux rappels qui sont passés à .then
et .catch
méthodes d'un Promise
.
Les promesses ont ouvert la voie à l'une des améliorations les plus intéressantes de JavaScript. ECMAScript 2017 a introduit du sucre syntaxique en plus des promesses en JavaScript sous la forme de async
et await
déclarations.
Ils nous permettent d'écrire du code basé sur Promise
comme s'il était synchrone, mais sans bloquer le thread principal, comme le montre cet exemple de code:
const verifyUser = async function(username, password){ try { const userInfo = await dataBase.verifyUser(username, password); const rolesInfo = await dataBase.getRoles(userInfo); const logStatus = await dataBase.logAccess(userInfo); return userInfo; }catch (e){ //handle errors as needed } };
En attente Promise
à résoudre n'est autorisé que dans async
fonctions ce qui signifie que verifyUser
devait être défini avec async function
.
Cependant, une fois ce petit changement effectué, vous pouvez await
tout Promise
sans modifications supplémentaires des autres méthodes.
Les fonctions asynchrones sont la prochaine étape logique dans l'évolution de la programmation asynchrone en JavaScript. Ils rendront votre code beaucoup plus propre et plus facile à maintenir. Déclarer une fonction comme async
s'assurera qu'il renvoie toujours un Promise
donc vous n’avez plus à vous en soucier.
Pourquoi devriez-vous commencer à utiliser le JavaScript async
fonctionne aujourd'hui?
try
/ catch
comme dans tout autre code synchrone..then
bloc ne passera pas au suivant .then
car il ne parcourt que le code synchrone. Mais, vous pouvez parcourir await
appels comme s’il s’agissait d’appels synchrones.Les instructions Async / Wait sont du sucre syntaxique créé en plus des promesses JavaScript. Ils nous permettent d'écrire du code basé sur Promise comme s'il était synchrone, mais sans bloquer le thread principal.
En JavaScript, le callback hell est un anti-pattern dans le code qui résulte d'une mauvaise structuration du code asynchrone. On le voit généralement lorsque les programmeurs essaient de forcer une structure visuelle descendante dans leur code JavaScript asynchrone basé sur le rappel.
Une promesse en JavaScript est comme une valeur d'espace réservé qui est censée finir par devenir la valeur finale du résultat réussi ou la raison de l'échec.