‫Instructeur : Prenons maintenant une minute pour en savoir plus
‫sur la nature asynchrone de Node. js, qui
‫inclut des sujets absolument fondamentaux,
‫comme le code synchrone, asynchrone, bloquant
‫et non bloquant.
‫Et tout cela sera vraiment important pour
‫comprendre tout ce qui se passe tout au long
‫de cette section.
‫Donc, ce morceau de code que nous avons écrit
‫dans la dernière leçon, pour lire un fichier puis enregistrer son contenu
‫dans une variable, était d'une manière dite
‫synchrone, ce qui signifie simplement
‫que chaque instruction est essentiellement traitée l'une après l'autre, ligne par ligne.
‫Dans cet exemple,
‫tout d'abord, le module du système de fichiers est
‫requis, puis le fichier est
‫lu, puis nous enregistrons le résultat dans la console.
‫Vous voyez donc que chaque ligne
‫de code attend essentiellement le résultat de la ligne précédente.
‫Maintenant, cela peut devenir un
‫problème, surtout avec des opérations
‫lentes, car chaque ligne bloque l'exécution du
‫reste du code.
‫Et donc, on dit que
‫le code synchrone est aussi appelé code bloquant car, encore une
‫fois, une certaine opération ne peut être
‫exécutée qu'une fois la précédente terminée.
‫Et à cause de la façon dont Node. js a été
‫conçu, cela devient un énorme problème, comme nous le verrons
‫en détail dans la diapositive suivante.
‫La solution à ce problème dans
‫Node consiste donc à utiliser du code asynchrone non bloquant.
‫Ainsi, dans le code asynchrone, nous téléchargeons un
‫travail lourd à traiter essentiellement en arrière-plan.
‫Et puis, une fois ce travail
‫terminé, une fonction de rappel que nous enregistrons
‫auparavant est appelée pour gérer le résultat.
‫Et pendant tout ce
‫temps, le reste du code peut toujours
‫s'exécuter sans être bloqué par la
‫lourde tâche, qui s'exécute désormais en arrière-plan.
‫Donc, cela signifie que nous pouvons effectivement différer ou
‫réagir dans le futur
‫afin de rendre le code non
‫bloquant et c'est, bien sûr, bien meilleur.
‫Logique?
‫Ainsi, dans cet exemple, nous utilisons
‫la fonction asynchrone readFile, qui accepte une fonction de rappel.
‫Cela commencera à lire le fichier
‫en arrière-plan, puis, passera immédiatement à l'instruction suivante, en
‫imprimant sur la console le fichier de lecture de chaîne.
‫Donc, encore une fois, voyez-vous, nous ne bloquons pas l'exécution ici.
‫Ensuite, lorsque le fichier est enfin complètement lu, la fonction
‫de rappel sera appelée, et
‫ainsi, les données qui ont été lues
‫seront alors imprimées sur la console.
‫C'est donc assez différent
‫de la version synchrone, n'est-ce pas ?
‫Maintenant, la question ici est,
‫pourquoi est-ce qu'il doit en être ainsi ?
‫Quel est le problème avec le blocage de l'exécution du code dans Node. js ?
‫Ou, en d'autres termes, pourquoi utilisons-nous si souvent le
‫rappel dans Node. js ?
‫Eh bien, découvrons.
‫Et pour comprendre ces questions, la première chose
‫que nous devons comprendre est le
‫fait qu'un Node. js, qui est
‫l'endroit où notre application s'exécute,
‫il n'y a qu'un seul thread.
‫Et le fil est comme un ensemble d'instructions exécutées
‫dans le processeur de l'ordinateur.
‫Donc, fondamentalement, le thread est l'endroit où
‫notre code est réellement exécuté dans le processeur d'une machine.
‫Alors, rappelez-vous, Node. js est essentiellement monothread
‫et donc, pour chaque application, il n'y a qu'un seul thread.
‫C'est juste la façon dont Node. js a été conçu.
‫Maintenant, cela signifie que tous les
‫utilisateurs qui accèdent à votre application utilisent tous le même
‫thread, donc, fondamentalement, accèdent au même thread.
‫Ainsi, chaque fois qu'ils interagissent avec l'application,
‫le code exécuté pour
‫chaque utilisateur sera exécuté dans le même thread au
‫même endroit sur l'ordinateur exécutant l'application.
‫Et cela est vrai, peu importe
‫si vous avez cinq utilisateurs, comme dans ce diagramme, ou 5 000 ou 5 millions.
‫Très bien?
‫Maintenant, cela signifie également que lorsqu'un utilisateur verrouille le
‫thread unique avec du code synchrone, comme
‫nous venons de
‫le voir, tous les autres utilisateurs devront
‫attendre la fin de cette exécution.
‫Et ce n'est peut-être pas un gros
‫problème si vous avez environ cinq utilisateurs, mais
‫ce sera certainement le cas pour des milliers, voire des
‫millions d'utilisateurs en même temps.
‫Donc, imaginez qu'un utilisateur accède à votre application et
‫qu'il y ait un énorme fichier synchrone lu dans votre
‫code qui prendra environ une seconde à charger.
‫Cela signifiera, bien sûr, que pendant cette
‫seconde, tous les autres
‫utilisateurs devront attendre car toute l'exécution est bloquée pendant cette seconde.
‫Ainsi, si ces autres utilisateurs souhaitent effectuer des tâches
‫simples, comme se connecter à votre
‫application ou simplement demander des données, ils ne pourront pas le faire.
‫Ils devront attendre la fin de la lecture du fichier.
‫Ce n'est que lorsque cela se produira qu'ils pourront enfin effectuer
‫les tâches les plus simples, l'une après l'autre.
‫Maintenant, veuillez noter qu'il s'agit d'une version très simplifiée de ce qui se passe
‫réellement dans les coulisses de Node. js, c'est
‫pourquoi nous reviendrons sur tout
‫cela dans la section suivante pour mieux
‫comprendre comment Node. js gère le code asynchrone sous le capot.
‫Mais à ce
‫stade, cela vous suffit pour comprendre le concept.
‫Il vaut mieux y aller étape par
‫étape et ne pas le
‫rendre trop confus dès le début, d'accord ?
‫Quoi qu'il en soit, c'est ainsi
‫que la situation se déroulerait avec du code de
‫blocage synchrone, ce qui est évidemment une expérience terrible pour vos utilisateurs.
‫Et donc, c'est vraiment votre travail en tant que
‫développeur d'éviter ce genre de situations
‫en utilisant du code asynchrone.
‫Donc, pour la même situation, nous devrions, bien sûr, utiliser
‫la fonction de lecture de fichier asynchrone, qui
‫au lieu de bloquer le seul
‫thread, fait le gros du travail en arrière-plan,
‫où elle reste essentiellement jusqu'à ce qu'elle
‫ait fini de lire les données du fichier.
‫Bien entendu, nous enregistrons ensuite également une fonction
‫de rappel à appeler une fois les données disponibles.
‫Et dans ce scénario, tous les autres
‫utilisateurs peuvent alors effectuer leurs tâches dans un
‫seul thread, l'un après l'autre, pendant que le fichier est
‫toujours lu en arrière-plan.
‫Maintenant, une fois les données lues, notre fonction de
‫rappel sera, bien sûr, appelée pour
‫être exécutée dans le thread unique principal afin de traiter les données lues.
‫Et c'est tout.
‫C'est un aperçu de la façon dont Node. js gère
‫le comportement asynchrone afin d'implémenter le modèle d'E/S
‫non bloquant dont nous avons parlé
‫dans la conférence d'introduction, d'accord ?
‫Et I/O signifie simplement entrée-sortie, ce qui revient
‫essentiellement à accéder au système de fichiers et
‫à gérer les requêtes réseau.
‫C'est en fait toute la raison pour laquelle Node. js est entièrement
‫conçu autour des rappels, comme vous le
‫verrez tout au long du cours.
‫Dans d'autres langages de programmation, comme PHP, cela
‫fonctionne très différemment car vous obtenez, en gros, un
‫nouveau thread pour chaque nouvel utilisateur,
‫ce qui est un paradigme
‫complètement différent et fonctionne vraiment complètement différemment.
‫Mais le créateur de Node. js a trouvé
‫que ce modèle était la meilleure solution pour créer
‫des applications Web hautement performantes et évolutives.
‫Maintenant, juste pour terminer ici, il est important de savoir
‫que, lorsque nous utilisons des
‫rappels dans notre code, cela ne le rend pas automatiquement asynchrone, d'accord ?
‫Donc, passer des fonctions à d'autres fonctions est
‫assez courant en JavaScript,
‫mais bien sûr, encore une fois, cela
‫ne les rend pas automatiquement asynchrones, d'accord ?
‫Cela ne fonctionne que de cette façon pour certaines fonctions de l'API Node,
‫telles que la fonction readFile
‫et bien d'autres, à mesure que les gens l'exploreront à l'avenir.
‫Et maintenant, juste pour
‫finir, puisque nous parlons ici de code asynchrone,
‫juste une dernière remarque sur les fonctions de rappel.
‫Ainsi, ce modèle de rappel dont nous venons de
‫parler, où une fonction
‫est appelée une fois que la précédente a
‫terminé son travail, peut rapidement conduire à un code difficile à lire et ingérable.
‫Prenons simplement cet exemple où le deuxième fichier
‫lu dépend du premier,
‫ensuite, le troisième fichier lu dépend du second,
‫et enfin, nous voulons utiliser les données finales
‫pour écrire un fichier en conséquence.
‫Cela semble assez déroutant, non?
‫Je veux dire, ça va très
‫bien fonctionner, mais c'est juste difficile à
‫raisonner et c'est juste avec quatre niveaux de profondeur.
‫Imaginez que vous ayez 10 ou 20 niveaux,
‫ce qui n'est en fait pas si rare.
‫Quoi qu'il en soit, c'est ce que nous appelons l'enfer du rappel.
‫C'est un problème tellement
‫courant qu'il a déjà son propre nom.
‫Et remarquez-vous cette forme triangulaire ici ?
‫C'est un signe très clair que vous êtes dans un enfer de rappel.
‫Maintenant, comment échapper à l'enfer des rappels ?
‫Eh bien, nous pouvons utiliser des outils plus
‫avancés pour gérer le code asynchrone,
‫comme les promesses ES6 ou encore mieux, ES8 async/await.
‫Maintenant, le modèle dont nous venons de
‫parler sera toujours le même.
‫Nous avons juste des manières
‫plus élégantes de traiter le code lui-même et de l'écrire.
‫Et il y a toute une section
‫facultative plus tard dans le cours sur les promesses et aussi, async/wait, donc
‫au cas où vous ne les connaissez pas.
‫Mais pour l'instant, nous continuerons à utiliser les rappels
‫car c'est ce que Node utilisait à l'origine et
‫autour duquel il a été conçu.
‫Et maintenant, cela étant
‫dit, passons à autre chose et utilisons ce modèle asynchrone
‫en pratique pour la première fois.