On parle beaucoup de programmation asynchrone, mais quel est exactement le gros problème? Le gros problème est que nous voulons que notre code soit non bloquant.
Les tâches pouvant bloquer notre application incluent les requêtes HTTP, l'interrogation d'une base de données ou l'ouverture d'un fichier. Certaines langues, comme Java, gèrent cela en créant plusieurs threads. Cependant, JavaScript n'a qu'un seul thread. Nous devons donc concevoir nos programmes de manière à ce qu'aucune tâche ne bloque le flux..
La programmation asynchrone résout ce problème. Cela nous permet d'exécuter des tâches plus tard, de manière à ne pas retarder la totalité du programme en attendant la fin des tâches. Cela nous aide également lorsque nous voulons nous assurer que les tâches s'exécutent de manière séquentielle.
Dans la première partie de ce didacticiel, nous allons apprendre les concepts du code synchrone et asynchrone et voir comment utiliser les fonctions de rappel pour résoudre les problèmes liés à l’asynchronie..
Je veux que vous vous souveniez de la dernière fois que vous êtes allé faire les courses à l'épicerie. Il y avait probablement plusieurs caisses enregistreuses ouvertes pour vérifier les clients. Cela aide le magasin à traiter plus de transactions dans le même temps. Ceci est un exemple de concurrence.
En termes simples, la simultanéité consiste à effectuer plusieurs tâches simultanément. Votre système d'exploitation est simultané car il exécute plusieurs processus simultanément. Un processus est un environnement d'exécution ou une instance d'une application en cours d'exécution. Par exemple, votre navigateur, votre éditeur de texte et votre logiciel antivirus sont tous des processus exécutés simultanément sur votre ordinateur..
Les applications peuvent également être concurrentes. Ceci est réalisé avec des threads. Je ne vais pas aller trop loin parce que cela dépasse le cadre de cet article. Si vous souhaitez une explication détaillée du fonctionnement de JavaScript sous le capot, je vous recommande de regarder cette vidéo..
Un thread est une unité au sein d'un processus qui exécute du code. Dans notre exemple de magasin, chaque ligne de paiement serait un fil de discussion. Si nous n’avions qu’une seule ligne de paiement dans le magasin, cela changerait la façon dont nous avons traité les clients..
Avez-vous déjà fait la queue et quelque chose a retardé votre transaction? Peut-être avez-vous eu besoin d'un contrôle de prix, ou dû voir un responsable. Lorsque je suis au bureau de poste et que j'essaie d'expédier un colis et que mes étiquettes ne sont pas remplies, le caissier me demande de m'éloigner tout en continuant de vérifier les autres clients. Quand je suis prêt, je retourne à l'avant de la ligne pour être vérifié.
Ceci est similaire au fonctionnement de la programmation asynchrone. Le caissier aurait pu m'attendre. Parfois ils le font. Mais c'est une meilleure expérience de ne pas tenir la ligne et de vérifier les autres clients. Le fait est que les clients ne doivent pas être extraits dans l'ordre où ils se trouvent. De même, le code ne doit pas nécessairement être exécuté dans l'ordre dans lequel nous l'écrivons.
Il est naturel de penser à notre code s’exécutant séquentiellement de haut en bas. C'est synchrone. Cependant, avec JavaScript, certaines tâches sont intrinsèquement asynchrones (par exemple, setTimeout), et certaines tâches que nous concevons comme étant asynchrones, car nous savons à l’avance qu’elles peuvent bloquer..
Jetons un coup d'oeil à un exemple pratique utilisant des fichiers dans Node.js. Si vous souhaitez essayer les exemples de code et que vous avez besoin d'une introduction à l'utilisation de Node.js, vous pouvez trouver des instructions de démarrage dans ce didacticiel. Dans cet exemple, nous allons ouvrir un fichier de messages et récupérer l'un des messages. Ensuite, nous allons ouvrir un fichier de commentaires et récupérer les commentaires pour ce post.
C'est la manière synchrone:
const fs = require ('fs'); const path = require ('path'); const postsUrl = path.join (__ dirname, 'db / posts.json'); const commentsUrl = path.join (__ dirname, 'db / comments.json'); // retourne les données de notre fichier function loadCollection (url) try const réponse = fs.readFileSync (url, 'utf8'); retourne JSON.parse (réponse); catch (error) console.log (error); // retourne un objet par id function getRecord (collection, id) return collection.find (function (element) return element.id == id;); // retourne un tableau de commentaires pour une fonction post getCommentsByPost (comments, postId) return comments.filter (function (comment) return, comment.postId == postId;); // code d'initialisation const posts = loadCollection (postsUrl); const post = getRecord (posts, "001"); const comments = loadCollection (commentsUrl); const postComments = getCommentsByPost (comments, post.id); console.log (post); console.log (postComments);
["id": "001", "title": "Message d'accueil", "text": "Hello World", "author": "Jane Doe", "id": "002", "title": "JavaScript 101", "text": "Principes fondamentaux de la programmation.", "Auteur": "Alberta Williams", "id": "003", "titre": "Programmation asynchrone", "text": " Callbacks, Promises et Async / Await. "," Author ":" Alberta Williams "]
["id": "phx732", "postId": "003", "text": "Je ne reçois pas ce type de rappel." , "id": "avj9438", "postId": "003", "text": "Ces informations sont vraiment utiles." , "id": "gnk368", "postId": "001", "text": "Ceci est un commentaire de test." ]
le readFileSync
méthode ouvre le fichier de manière synchrone. Par conséquent, nous pouvons également écrire notre code d'initialisation de manière synchrone. Mais ce n’est pas la meilleure façon d’ouvrir le fichier car c’est une tâche potentiellement bloquante. L'ouverture du fichier doit être effectuée de manière asynchrone afin que le flux d'exécution puisse être continu..
Le noeud a un readFile
méthode, nous pouvons utiliser pour ouvrir le fichier de manière asynchrone. C'est la syntaxe:
fs.readFile (url, 'utf8', fonction (erreur, données) …);
Nous pouvons être tentés de renvoyer nos données dans cette fonction de rappel, mais nous ne pourrons pas les utiliser dans notre loadCollection
une fonction. Notre code d'initialisation devra également changer car nous n'aurons pas les bonnes valeurs à affecter à nos variables.
Pour illustrer le problème, examinons un exemple plus simple. Que pensez-vous que le code suivant va imprimer?
function task1 () setTimeout (function () console.log ('premier');, 0); function task2 () console.log ('second'); function task3 () console.log ('third'); tache 1(); task2 (); task3 ();
Cet exemple affichera «deuxième», «troisième», puis «premier». Peu importe que le setTimeout
la fonction a un délai de 0. C'est une tâche asynchrone en JavaScript, elle sera donc toujours différée pour s'exécuter plus tard. le première tâche
fonction peut représenter n’importe quelle tâche asynchrone, comme ouvrir un fichier ou interroger notre base de données.
Une solution pour que nos tâches soient exécutées dans l'ordre que nous voulons est d'utiliser des fonctions de rappel..
Pour utiliser les fonctions de rappel, vous transmettez une fonction en tant que paramètre à une autre fonction, puis appelez la fonction lorsque votre tâche est terminée. Si vous avez besoin d'une introduction à l'utilisation de fonctions d'ordre supérieur, reactivex propose un didacticiel interactif que vous pouvez essayer..
Les rappels nous permettent de forcer les tâches à s'exécuter de manière séquentielle. Ils nous aident également lorsque nous avons des tâches qui dépendent des résultats d’une tâche précédente. À l’aide des rappels, nous pouvons corriger notre dernier exemple afin qu’il affiche «premier», «deuxième», puis «troisième»..
function first (cb) setTimeout (function () return cb ('first');, 0); function second (cb) return cb ('second'); function third (cb) return cb ('third'); premier (fonction (résultat1) console.log (résultat1); deuxième (fonction (résultat2) console.log (résultat2); troisième (fonction (résultat3) console.log (résultat3);) )
Au lieu d'imprimer la chaîne dans chaque fonction, nous retournons la valeur dans un rappel. Lorsque notre code est exécuté, nous imprimons la valeur qui a été transmise à notre rappel. C'est ce que notre readFile
fonction utilise.
Pour revenir à notre exemple de fichiers, nous pouvons changer notre loadCollection
fonction de sorte qu'il utilise des rappels pour lire le fichier de manière asynchrone.
function loadCollection (url, rappel) fs.readFile (url, 'utf8', fonction (erreur, données) if (erreur) console.log (erreur); else retour appel (JSON.parse (données)) ;);
Et voici à quoi notre code d'initialisation ressemblera à l'aide de rappels:
loadCollection (postsUrl, function (posts) loadCollection (commentsUrl, function (comments) getRecord (posts, "001", function (post) const postComments = getCommentsByPost (commentaires, post.id); console.log (post); console.log (postComments););););
Une chose à noter dans notre loadCollection
la fonction est qu'au lieu d'utiliser un essayer / attraper
déclaration pour gérer les erreurs, nous utilisons un sinon
déclaration. Le bloc catch ne pourra pas capturer les erreurs renvoyées par le readFile
rappeler.
Il est recommandé d’avoir des gestionnaires d’erreur dans notre code pour les erreurs résultant d’influences extérieures par opposition aux bogues de programmation. Cela inclut l’accès aux fichiers, la connexion à une base de données ou la création d’une requête HTTP..
Dans l'exemple de code révisé, je n'ai inclus aucune gestion d'erreur. Si une erreur devait se produire à l’une des étapes, le programme ne se poursuivrait pas. Il serait bien de donner des instructions utiles.
Un exemple de cas dans lequel la gestion des erreurs est importante est si nous avons une tâche pour connecter un utilisateur. Cela implique d'obtenir un nom d'utilisateur et un mot de passe à partir d'un formulaire, d'interroger notre base de données pour savoir s'il s'agit d'une combinaison valide, puis de rediriger l'utilisateur vers son tableau de bord si nous réussissons. Si le nom d'utilisateur et le mot de passe étaient invalides, notre application cesserait de fonctionner si nous ne lui disions pas quoi faire..
Une meilleure expérience utilisateur consisterait à renvoyer un message d'erreur à l'utilisateur et à lui permettre de réessayer de se connecter. Dans notre exemple de fichier, nous pourrions transmettre un objet d'erreur dans la fonction de rappel. C'est le cas avec le readFile
une fonction. Ensuite, lorsque nous exécutons le code, nous pouvons ajouter un sinon
déclaration pour gérer le résultat positif et le résultat rejeté.
À l'aide de la méthode de rappel, écrivez un programme qui ouvrira un fichier d'utilisateurs, sélectionnera un utilisateur, puis ouvrira un fichier de publications et imprimera les informations de l'utilisateur et toutes ses publications..
La programmation asynchrone est une méthode utilisée dans notre code pour différer des événements pour une exécution ultérieure. Lorsque vous traitez avec une tâche asynchrone, les rappels constituent une solution pour chronométrer nos tâches afin qu'elles s'exécutent de manière séquentielle..
Si plusieurs tâches dépendent du résultat des tâches précédentes, une solution consiste à utiliser plusieurs rappels imbriqués. Cependant, cela pourrait conduire à un problème appelé «enfer de rappel». Les promesses résolvent le problème de l'appel de rappel, et les fonctions asynchrones nous permettent d'écrire notre code de manière synchrone. Dans la partie 2 de ce tutoriel, nous allons apprendre ce qu’ils sont et comment les utiliser dans notre code.