Voici une série d'articles qui vous permettra créer des applications backend en Javascript.
Node.js est aujourd'hui un incontournable, il est donc essentiel pour un développeur de le maitriser.
Je vais donc publier un nouvel article environ au deux jours et petit à petit vous apprendrez tout ce qu'il y a à savoir sur Node.js
Pour ne rien manquer suivez moi sur twitter : https://twitter.com/EricLeCodeur
Comment fonctionne NodeJS sous le capot ?
Dans cette section nous allons faire un peu de théorie et découvrir comment NodejS exécute son code Javascript.
Comme vous le savez, NodeJS permet d'exécuter du code asynchrone. Ce concept peu semblé simple mais en arrière plan c'est un peu plus compliqué. Qu'est-ce qui détermine quel code est exécuté ? Qu'est-ce qui détermine l'ordre d'exécution ?
Comprendre ces concepts est essentiel pour développer avec NodeJS. Pas besoin de devenir un expert sur le sujet mais au moins comprendre la base.
À noter que certain concepts ont été simplifié afin de permettre de mieux les expliquer.
L'architecture de NodeJS
NodeJS est composé de deux parties principales l'engin V8 et la librairie libuv
L'engin V8
S'occupe de convertir le code Javascript en code machine. Une fois le code convertie en code machine l'exécution sera géré par la librairie libuv
libuv
Est une librairie open-source, écrite en c++ qui se spécialise dans l'exécution asynchrone i/o (ex. File system, Networking et plus)
libuv implémente deux features très important de NodeJS soit le Event Loop et le Thread Pool
Un point important à comprendre c'est que NodeJS fonctionne en mode single thread.
C'est à dire qu'il peux exécuter seulement une tâche à la fois. Si une tâche demande trop de temps/ressource alors elle va bloquer/empêcher les autres tâches de s'exécuter.
Imaginez, par exemple, si il y avait 100 000 usagers sur le site en même temps qui demandait l'accès à la base de donnée, le temps de réponse deviendrait vite inacceptable. C'est pourquoi NodeJS a besoin d'une gestion efficace de l'exécution du code asynchrone.... Ça c'est le travail du Event Loop
Le Event Loop permet de gérer le code asynchrone comme les callbacks, les promesses et requêtes network qui demande peu de resource. Et quand une tâche est trop longue à exécuter, afin de ne pas bloquer le thread, le Event Loop va déléguer ce travail au Thread Pool.
Le Thread Pool peut quand a lui exécuter des tâches en parallèle et s'occupe donc des tâches plus lourdes comme l'accès au file system et les processus très demandant comme par exemple les conversions de vidéo ou la cryptographie.
Ordre d'exécution d'une application NodeJS
Lorsque l'on exécute une application NodeJS, le code d'initialization, les requires et le code "top level" sont exécuté immédiatement un après l'autre.
Les callbacks rencontré dans notre code ne sont pas exécuté immédiatement car potentiellement bloquant, il bloquerait l'application aux autres tâches et aux autres utilisateurs. Ces callbacks sont donc enregistré auprès du Event Loop
Une fois le code "top level" exécuté, NodeJS donnera la main au Event Loop afin qu'il puisse exécuter les tâches qu'il contient.
Le Event Loop décide, selon des critères pré-définit, quel ordre d'exécution devra être respecté. Le Event Loop peut également décidé de déléguer une tâche vraiment longue au Thread Pool. (ex. accès au file system).
Le Thread Pool lui peut exécuter plusieurs tâche en même temps (multi-thread) et retournera le résultat au Event Loop
Tant et aussi longtemps qu'il y a des tâches à exécuter, le Event Loop va garder l'application active.
Une fois toutes les tâches du Event Loop terminé, le contrôle est re-donné au Thread principal de votre application qui terminera le programme.
NodeJS en exemple
C'est bien beau la théorie mais revoyons le tout cette fois ci avec un exemple concret
const fs = require('fs')
console.log('Début de la première tâche')
fs.readFile('./data/products.json', 'utf8', (err, data) => {
console.log(data)
console.log('Première tâche terminé')
})
console.log('Début de la deuxième tâche')
Résultat
Début de la première tâche
Début de la deuxième tâche
{
"name": "iPhone 12",
"price": 900
}
Première tâche terminé
Basé sur la logique expliquer plus tôt, NodeJS exécutera le code dans l'ordre suivant :
→ const fs = require(fs)
→ console.log('Début de la première tâche')
→ enregistrement du callback readFile avec le Event Loop
→ console.log('Début de la deuxième tâche')
→ Tâches de haut niveau terminé la main est donc passé au Event Loop
→ readFile callback → Déléguer au Thread Pool
→ Quand le readFile est terminé
→ console.log(data)
→ console.log('Première tâche terminé')
→ Si aucune autre tâche en attente alors termine le Event Loop
→ Fin du programme
Exemple avec SetTimeout zéro
console.log('Premier')
setTimeout(() => {
console.log('Deuxième')
}, 0)
console.log('Troisième')
Résultat
Premier
Troisième
Deuxième
Ici on aurait pu penser qu'avec un setTimeOut de 0 il serait exécuté immédiatement ? Mais non, comme vu précédemment, NodeJS envoi les callback au Event Loop et exécute le code top level en premier.
Basé sur cette logique, le NodeJS exécutera le code dans l'ordre suivant :
→ console.log('Premier')
→ register setTimeout callback avec le Event Loop
→ console.log('Troisième')
→ Passe la main au Event Loop
→ callback setTimeout
→ console.log('Deuxième')
→ Si pas d'autre tache alors termine le Event Loop
→ Fin du programme
Exemple serveur
const http = require('http')
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.end('<h1>Home page</h1>')
} else if (req.url === '/about') {
res.end('<h1>About page</h1>')
let i = 0
do {
i++
} while (i < 10000000000)
} else {
res.end('page not found')
}
})
server.listen(5000, 'localhost', () => {
console.log('Server is listening at localhost on port 5000')
})
Il y a deux enseignement à retirer de cet exemple. Premièrement, l'application NodeJS ne va jamais s'arrêter. Le Event Loop est sans fin puisqu'il attend les events du serveur. La fonction 'listen' garde le Event Loop actif.
Enfin, lorsque un usager va visiter la page about, Node va exécuter le 'do while' et comme ce n'est pas du code asynchrone l'accès au site web sera temporairement bloqué pour tous les usagers jusqu'a temps que le do while se termine. Cela est un bon exemple du fait que NodeJS est single thread et que il faut faire attention comment vous codez votre application.
Par exemple, dans ce cas ci, il serait préférable de placer le do while a l'intérieur d'une fonction async afin de ne pas bloquer le thread.
Conclusion
C'est tout pour aujourd'hui, suivez moi sur twitter : https://twitter.com/EricLeCodeur afin d'être avisé de la parution du prochain article (d'ici deux jours).
Top comments (0)