JavaScript – des rappels à async / wait

JavaScript est synchrone. Cela signifie qu’il exécutera votre bloc de code par ordre après le levage. Avant que le code ne s’exécute, les déclarations var et function sont «hissées» en haut de leur portée.

Voici un exemple de code synchrone:

Ce code enregistrera de manière fiable «1 2 3».

Les requêtes asynchrones attendront la fin d’un minuteur ou une requête pour répondre pendant que le reste du code continue à s’exécuter. Ensuite, lorsque le moment sera venu, un rappel déclenchera ces requêtes asynchrones.

Voici un exemple de code asynchrone:

Cela enregistrera en fait «1 3 2», puisque le «2» est sur un setTimeout qui ne s’exécutera, par cet exemple, qu’après deux secondes. Votre application ne se bloque pas en attendant la fin des deux secondes. Au lieu de cela, il continue d’exécuter le reste du code et lorsque le délai d’expiration est terminé, il revient à afterTwoSeconds.

Vous pouvez demander “Pourquoi est-ce utile?” ou “Comment puis-je synchroniser mon code asynchrone?”. J’espère que je pourrai vous montrer les réponses.

“Le problème”

Disons que notre objectif est de rechercher un utilisateur GitHub et d’obtenir tous les dépôts de cet utilisateur. Le fait est que nous ne connaissons pas le nom exact de l’utilisateur. Nous devons donc lister tous les utilisateurs avec un nom similaire et leurs référentiels respectifs.

N’a pas besoin de super fantaisie, quelque chose comme ça

Dans ces exemples, le code de requête utilisera XHR (XMLHttpRequest). Vous pouvez le remplacer par jQuery $ .ajax ou par l’approche native plus récente appelée fetch . Les deux vous donneront l’approche des promesses dès le départ.

Il sera légèrement modifié en fonction de votre approche mais en guise de point de départ:

N’oubliez pas que dans ces exemples, la partie importante n’est pas le résultat final du code. Au lieu de cela, votre objectif devrait être de comprendre les différences entre les approches et comment vous pouvez les exploiter pour votre développement.

Vous pouvez enregistrer une référence de fonction dans une variable lorsque vous utilisez JavaScript. Ensuite, vous pouvez les utiliser comme arguments d’une autre fonction à exécuter plus tard. Ceci est notre “rappel”.

Un exemple serait:

L’utilisation du callback pour résoudre notre problème nous permet de faire quelque chose comme ceci à la fonction request que nous avons définie précédemment:

Notre fonction pour la requête acceptera désormais un callback afin que lorsqu’une requête est faite, elle soit appelée en cas d’erreur et en cas de succès.

Décomposer ceci:

Remarque : envoyer d’abord l’erreur en tant que paramètre est une pratique courante, en particulier lors de l’utilisation de Node.js.

Une approche plus «complète» et plus lisible consisterait à gérer les erreurs. Nous garderions le rappel séparé de l’exécution de la requête.

Quelque chose comme ça:

Cela finit par avoir des problèmes comme les courses et les problèmes de gestion des erreurs. La course a lieu lorsque vous ne contrôlez pas l’utilisateur que vous obtiendrez en premier. Nous demandons les informations pour chacun d’entre eux au cas où il y en aurait plus d’un. Nous ne prenons pas en compte une commande. Par exemple, l’utilisateur 10 peut venir en premier et l’utilisateur 2 en dernier. Nous avons une solution possible plus loin dans l’article.

Le principal problème avec les rappels est que la maintenance et la lisibilité peuvent devenir pénibles. C’est déjà le cas et le code ne fait pratiquement rien. C’est ce qu’on appelle l ‘ enfer des rappels qui peut être évité avec notre prochaine approche.

Vous promet de rendre votre code plus lisible. Un nouveau développeur peut accéder à la base de code et voir un ordre d’exécution clair de votre code.

Pour créer une promesse, vous pouvez utiliser:

Décomposons-le:

Points à garder à l’esprit:

Remarque : Vous pouvez créer des promesses sans la fonction au moment des déclarations. La façon dont je le montre n’est qu’une manière courante de le faire.

«Théorie, théorie, théorie… je suis confus», pourriez-vous dire.

Prenons notre exemple de demande avec une promesse d’essayer de clarifier les choses:

Dans ce scénario, lorsque vous exécutez la requête , cela renverra quelque chose comme ceci:

C’est ainsi que nous résolvons les courses et certains des problèmes de gestion des erreurs. Le code est encore un peu compliqué. Mais c’est une façon de vous montrer que cette approche peut également créer des problèmes de lisibilité.

Une solution rapide serait de séparer les rappels comme ceci:

En regardant ce que userRequest attend dans l’ordre avec le .then , vous pouvez avoir une idée de ce que nous attendons de ce bloc de code. Tout est plus ou moins séparé par la responsabilité.

C’est «gratter la surface» de ce que sont les promesses. Pour avoir un bon aperçu de leur fonctionnement, je ne saurais trop recommander cet article.

Une autre approche consiste à utiliser les générateurs. C’est un peu plus avancé, donc si vous débutez, n’hésitez pas à passer au sujet suivant.

Une utilisation des générateurs est qu’ils vous permettent d’avoir un code asynchrone ressemblant à une synchronisation.

Ils sont représentés par un * dans une fonction et ressemblent à quelque chose comme:

Au lieu de renvoyer avec un return , les générateurs ont une instruction yield . Il arrête l’exécution de la fonction jusqu’à ce qu’un .next soit créé pour cette itération de fonction. C’est similaire à la promesse .then qui ne s’exécute qu’une fois résolue revient.

Notre fonction de requête ressemblerait à ceci:

Nous voulons avoir l ‘ url comme argument. Mais au lieu d’exécuter la requête hors de la porte, nous ne le voulons que lorsque nous avons un rappel pour gérer la réponse.

Notre générateur serait quelque chose comme:

Cela:

Une exécution de ceci serait donc:

Nous pourrions séparer les fonctions de rappel comme nous l’avons fait précédemment. Vous obtenez maintenant l’accord, une chose à retenir est que nous pouvons désormais gérer chaque liste de référentiels d’utilisateurs individuellement.

J’ai des opinions mitigées sur les générateurs. D’une part, je peux comprendre ce que l’on attend du code en regardant le générateur.

Mais son exécution finit par avoir des problèmes similaires à l’enfer des rappels.

Comme async / await, un compilateur est recommandé. En effet, il n’est pas pris en charge dans les anciennes versions de navigateur.

De plus, ce n’est pas si courant dans mon expérience. Cela peut donc générer de la confusion dans les bases de code maintenues par divers développeurs.

Un aperçu impressionnant du fonctionnement des générateurs peut être trouvé dans cet article. Et voici une autre excellente ressource.

<₹Async/Await

Cette méthode ressemble à un mélange de générateurs avec des promesses. Il vous suffit d’indiquer à votre code quelles fonctions doivent être asynchrones . Et quelle partie du code devra attendre pour que cette promesse se termine.

Dans ce scénario:

En appliquant ceci à notre demande , nous la laissons comme promesse comme vu précédemment:

Nous créons notre fonction async avec les attentes nécessaires comme ceci:

Nous avons maintenant une fonction asynchrone list qui gérera les requêtes. Un autre async est nécessaire dans le forEach afin que nous ayons la liste des dépôts que chaque utilisateur doit manipuler.

Nous l’appelons ainsi:

Cette approche et celle des promesses sont mes préférées car le code est facile à lire et à modifier. Vous pouvez en savoir plus sur async / await plus en détail ici.

Un inconvénient de l’utilisation de async / await est qu’il n’est pas pris en charge dans le front-end par les navigateurs plus anciens ou dans le back-end. Vous devez utiliser le Node 8.

Vous pouvez utiliser un compilateur comme babel pour résoudre ce problème.

Vous pouvez voir le code de fin réalisant notre objectif initial en utilisant async / await dans cet extrait.

Une bonne chose à faire est de l’essayer vous-même sous les différentes formes référencées dans cet article.

Selon le scénario que vous pourriez utiliser:

C’est à vous ce qui correspond à vos objectifs. Et ce qui vous permet de maintenir le code pour qu’il soit compréhensible pour les autres et pour votre futur moi.

Remarque: Toutes les approches deviennent légèrement moins verbeuses lors de l’utilisation d’alternatives pour des requêtes telles que $ .ajax et fetch .

Dites-moi ce que vous feriez de différentes manières que vous avez trouvées pour rendre chaque approche plus lisible.

Ceci est l’article 11 sur 30. Il fait partie d’un projet de publication d’un article au moins une fois par semaine, des idées oisives aux tutoriels. Laisse un commentaire, suis-moi sur Diogo Spínola et reviens sur ton brillant projet!