web-dev-qa-db-fra.com

Qu'est-ce qu'un tick de boucle d'événement Node.js exactement?

Je suis entré de plus en plus dans les composants internes de l'architecture Node.js, et un terme que je vois venir souvent est "tick" comme dans "tick suivant de la boucle d'événement" ou la fonction nextTick () .

Ce que je n'ai pas vu, c'est une définition solide de ce qu'est exactement un "tick". Basé sur divers articles ( comme celui-ci ), j'ai pu reconstituer un concept dans ma tête, mais je ne suis pas sûr de sa précision.

Puis-je obtenir une description précise et détaillée d'un tick de boucle d'événement Node.js?

63
d512

N'oubliez pas que bien que JavaScript soit monothread, toutes les E/S du nœud et les appels aux API natives sont soit asynchrones (à l'aide de mécanismes spécifiques à la plate-forme), soit exécutés sur un thread distinct. (Tout cela est géré par libuv.)

Ainsi, lorsque des données sont disponibles sur un socket ou qu'une fonction API native est retournée, nous avons besoin d'un moyen synchronisé pour appeler la fonction JavaScript qui s'intéresse à l'événement particulier qui vient de se produire.

Il n'est pas sûr d'appeler simplement la fonction JS à partir du thread où l'événement natif s'est produit pour les mêmes raisons que celles rencontrées dans une application multithread régulière - conditions de concurrence, accès à la mémoire non atomique, etc.

Donc, ce que nous faisons, c'est placer l'événement dans une file d'attente de manière thread-safe. Dans un pseudo-code simplifié, quelque chose comme:

lock (queue) {
    queue.Push(event);
}

Ensuite, de retour sur le thread JavaScript principal (mais du côté C des choses), nous faisons quelque chose comme:

while (true) {
    // this is the beginning of a tick

    lock (queue) {
        var tickEvents = copy(queue); // copy the current queue items into thread-local memory
        queue.empty(); // ..and empty out the shared queue
    }

    for (var i = 0; i < tickEvents.length; i++) {
        InvokeJSFunction(tickEvents[i]);
    }

    // this the end of the tick
}

La while (true) (qui n'existe pas réellement dans le code source du nœud; c'est purement illustratif) représente la boucle d'événement . Le for interne appelle la fonction JS pour chaque événement qui était dans la file d'attente.

C'est une tique: l'invocation synchrone de zéro ou plusieurs fonctions de rappel associées à des événements externes. Une fois que la file d'attente est vidée et que la dernière fonction revient, le tick est terminé. Nous revenons au début (la coche suivante) et vérifions les événements qui ont été ajoutés à la file d'attente à partir d'autres threads pendant que notre JavaScript était en cours d'exécution .

Qu'est-ce qui peut ajouter des choses à la file d'attente?

  • process.nextTick
  • setTimeout/setInterval
  • E/S (éléments de fs, net, etc.)
  • crypto fonctions gourmandes en processeur comme les flux cryptographiques, pbkdf2 et le PRNG (qui sont en fait un exemple de ...)
  • tous les modules natifs qui utilisent file d'attente de travail libuv pour que les appels de bibliothèque C/C++ synchrones semblent asynchrones
127
josh3736

Une réponse plus simple pour les nouveaux utilisateurs de JavaScript:

La première chose à comprendre est que JavaScript est un "environnement monothread". Cela fait référence au comportement de JavaScript d'exécuter vos blocs de code un par un à partir de "la boucle d'événement" sur un seul thread. Ci-dessous, il y a une implémentation rudimentaire de la boucle d'événements tirée du livre de Kyle Simpson ydkJS et ensuite, une explication:

// `eventLoop` is an array that acts as a queue (first-in, first-out)
var eventLoop = [ ];
var event;

// keep going "forever"
while (true) {
    // perform a "tick"
    if (eventLoop.length > 0) {
        // get the next event in the queue
        event = eventLoop.shift();

        // now, execute the next event
        try {
            event();
        }
        catch (err) {
            reportError(err);
        }
    }
}

La première boucle while simule la boucle d'événement. Une coche est le retrait d'un événement de la "file d'attente de boucles d'événements" et l'exécution dudit événement.

Veuillez consulter la réponse de "Josh3796" pour une explication plus détaillée de ce qui se passe lors de la mise en file d'attente et de l'exécution d'un événement.

Je recommande également de lire le livre de Kyle Simpson pour ceux qui souhaitent acquérir une compréhension approfondie de JavaScript. Il est entièrement gratuit et open source et peut être trouvé à ce lien: https://github.com/getify/You-Dont-Know-JS

La section spécifique que j'ai référencée peut être trouvée ici: https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch1.md#event- boucle

5
Parm