Cet article partage avec vous les informations pertinentes sur les timers dans Node.js. Il est très complet et détaillé. Les amis dans le besoin peuvent s'y référer.
Implémentation du timer dans Node.js
Comme mentionné dans le billet de blog précédent, le timer dans Node n'est pas implémenté en ouvrant un nouveau fil de discussion, mais directement dans le boucle d'événements. Ce qui suit utilise plusieurs exemples de minuterie JavaScript et le code source associé à Node pour analyser la façon dont la fonction de minuterie est implémentée dans Node.
Caractéristiques de la fonction timer en JavaScript
Qu'il s'agisse de Node ou du navigateur, il existe deux fonctions timer, setTimeout et setInterval, et leurs caractéristiques de fonctionnement sont basiques. pareil, donc seul Node est utilisé comme exemple pour l’analyse ci-dessous.
Nous savons que la minuterie en JavaScript n'est pas différente de l'interruption planifiée sous-jacente de l'ordinateur. Lorsqu'une interruption arrive, le code en cours d'exécution sera interrompu et la fonction de traitement d'interruption planifiée sera exécutée. Lorsque le délai JavaScript expire, si aucun code n'est en cours d'exécution dans le thread d'exécution en cours, la fonction de rappel correspondante sera exécutée ; s'il y a du code en cours d'exécution, le moteur JavaScript n'interrompra pas le code en cours pour exécuter le rappel, ni start Le nouveau thread exécute le rappel, mais il est traité après l'exécution du code actuel.
console.time('A') setTimeout(function () { console.timeEnd('A'); }, 100); var i = 0; for (; i < 100000; i++) { }
En exécutant le code ci-dessus, vous pouvez voir que le temps de sortie final n'est pas d'environ 100 ms, mais de quelques secondes. Cela montre que la fonction de rappel planifiée n'est effectivement pas exécutée avant la fin de la boucle, mais est reportée jusqu'à la fin de la boucle. En fait, lors de l'exécution du code JavaScript, tous les événements ne peuvent pas être traités, et les nouveaux événements ne peuvent pas être traités tant que le code actuel n'est pas terminé. C'est pourquoi le navigateur ne répond plus lors de l'exécution d'un code JavaScript fastidieux dans le navigateur. Afin de faire face à cette situation, nous pouvons utiliser la technique Yielding Processes pour diviser le code fastidieux en petits morceaux (morceaux) Une fois que chaque morceau est traité, setTimeout est exécuté une fois et il est convenu que le morceau suivant sera exécuté. traités après une courte période de temps. Pendant cette période. Pendant le temps d'inactivité, le navigateur/le nœud peut traiter les événements en file d'attente.
Informations supplémentaires
Il y a une discussion plus détaillée des minuteries avancées et des processus de rendement au chapitre 22 Techniques avancées de programmation avancée JavaScript, troisième édition.
Implémentation de la minuterie dans Node
Initialisation par libuv du type uv_loop_t
Le billet de blog précédent mentionnait que Node appellerait la fonction uv_run de libuv pour démarrer default_loop_ptr Pour l'événement planification, default_loop_ptr pointe vers une variable default_loop_struct de type uv_loop_t. Lorsque Node démarre, il appellera uv_loop_init(&default_loop_struct) pour l'initialiser. L'extrait de la fonction uv_loop_init est le suivant :
int uv_loop_init(uv_loop_t* loop) { ... loop->time = 0; uv_update_time(loop); ... }
Vous pouvez voir que le Le champ temporel de la boucle est attribué en premier à 0, puis appelle la fonction uv_update_time, qui attribuera le dernier temps de comptage à loop.time.
Une fois l'initialisation terminée, default_loop_struct.time a une valeur initiale, et les opérations liées au temps seront comparées à cette valeur pour déterminer s'il faut appeler la fonction de rappel correspondante.
Cœur de planification d'événements de libuv
Comme mentionné précédemment, la fonction uv_run est la partie centrale de la bibliothèque libuv pour implémenter la boucle d'événements. Voici son organigramme :
Voici une brève description de la logique ci-dessus liée au timer :
Mettre à jour le champ horaire de la boucle actuelle, qui marque le "maintenant" sous le concept de boucle actuel ;
Vérifiez si la boucle est vivante, c'est-à-dire vérifiez s'il y a des tâches (gestionnaires/requêtes) qui doivent être traitées dans la boucle. il n'est pas nécessaire de faire une boucle ;
Vérifiez la minuterie enregistrée, si une certaine minuterie L'heure spécifiée dans est en retard sur l'heure actuelle, indiquant que la minuterie a expiré, donc sa fonction de rappel correspondante est exécutée
exécute I/ ; O polling (c'est-à-dire bloquer le thread et attendre que l'événement d'E/S se produise). Si ce qui suit Si aucune E/S n'est terminée à l'expiration d'un minuteur, il arrête d'attendre et exécute le rappel du minuteur suivant.
Si un événement d'E/S se produit, le rappel correspondant est exécuté ; puisqu'un autre minuteur peut expirer pendant le temps d'exécution du rappel, le minuteur doit être vérifié à nouveau et le rappel exécuté.
(En fait (4.) ici, c'est plus compliqué, pas seulement une opération en une seule étape. Cette description vise simplement à ne pas impliquer d'autres détails et à se concentrer sur l'implémentation de la minuterie.)
Le nœud continuera d'appeler uv_run jusqu'à ce que le la boucle n'est plus vivante.
timer_wrap et timers dans Node
Il existe une classe TimerWrap dans Node, qui est enregistrée en tant que module timer_wrap dans Node.
NODE_MODULE_CONTEXT_AWARE_BUILTIN(timer_wrap, node::TimerWrap::Initialize)
La classe TimerWrap est essentiellement une encapsulation directe de uv_timer_t, et NODE_MODULE_CONTEXT_AWARE_BUILTIN est une macro utilisée par Node pour enregistrer les modules intégrés.
Après cette étape, JavaScript peut faire fonctionner ce module. Le fichier src/lib/timers.js utilise JavaScript pour encapsuler la fonction timer_wrap et exporte exports.setTimeout, exports.setInterval, exports.setImmediate et d'autres fonctions.
Démarrage du nœud et initialisation globale
L'article précédent mentionnait que Node chargerait l'environnement d'exécution LoadEnvironment(env) au démarrage. Une étape très importante dans cette fonction consiste à charger src/node.js et à l'exécuter, src/node.js chargera le spécifié. module Et initialiser global et processus. Bien entendu, des fonctions telles que setTimeout seront également liées à l'objet global par src/node.js.
Ce qui précède représente l’intégralité du contenu de cet article, j’espère que vous l’aimerez tous.
Pour plus d'articles liés aux timers dans Node.js, veuillez faire attention au site Web PHP chinois !