Comment setTimeout fonctionne-t-il dans Node.JS?

112

Je suppose qu'une fois qu'il est exécuté, il est dans la file d'attente, mais dans la file d'attente, y a-t-il une assurance qu'il se déclenchera exactement après X millisecondes? Ou d'autres tâches lourdes plus élevées dans la file d'attente le retarderont-elles?

MyCodeGone
la source
14
Aucun système d'exploitation non temps réel ne permettra jamais de garantir une précision sérieuse. Toutes sortes de choses sur le système peuvent (et vont) gêner le mécanisme de minuterie.
Pointy

Réponses:

174

La sémantique de setTimeout est à peu près la même que dans un navigateur web: le timeout arg est un nombre minimum de ms à attendre avant de s'exécuter, pas une garantie. De plus, passer 0, un non-nombre ou un nombre négatif le fera attendre un nombre minimum de ms. Dans Node, cela fait 1 ms, mais dans les navigateurs, cela peut aller jusqu'à 50 ms.

La raison en est qu'il n'y a pas de préemption de JavaScript par JavaScript. Prenons cet exemple:

setTimeout(function () {
  console.log('boo')
}, 100)
var end = Date.now() + 5000
while (Date.now() < end) ;
console.log('imma let you finish but blocking the event loop is the best bug of all TIME')

Le flux ici est:

  1. planifier le délai d'expiration de 100 ms.
  2. busywait pendant 5000 ms.
  3. revenir à la boucle d'événements. vérifier les minuteries en attente et exécuter.

Si ce n'était pas le cas, alors vous pourriez avoir un bit de JavaScript "interrompre" un autre. Nous aurions à mettre en place des mutex et des sémaphores et autres, pour éviter qu'un code comme celui-ci soit extrêmement difficile à raisonner:

var a = 100;
setTimeout(function () {
  a = 0;
}, 0);
var b = a; // 100 or 0?

Le caractère unique de l'exécution JavaScript de Node le rend beaucoup plus simple à utiliser que la plupart des autres styles de concurrence. Bien sûr, le compromis est qu'il est possible pour une partie mal comportée du programme de bloquer le tout avec une boucle infinie.

Est-ce un meilleur démon à combattre que la complexité de la préemption? Ça dépend.

Isaacs
la source
20
Vous obtenez un +1 pour le console.logmessage exceptionnellement précis .
Fonder
J'aime la façon dont vous avez mis TIME dans votre chaîne. Très bonne explication !!!
Alisson
43

L'idée du non-blocage est que les itérations de boucle sont rapides. Ainsi, l'itération pour chaque tick devrait prendre suffisamment de temps pour que setTimeout soit précis avec une précision raisonnable (peut-être <100 ms environ).

En théorie, vous avez raison. Si j'écris une application et bloque la coche, setTimeouts sera retardé. Donc, pour répondre à votre question, qui peut assurer l'exécution de setTimeouts à temps? En écrivant un code non bloquant, vous pouvez contrôler le degré de précision jusqu'à presque n'importe quel degré de précision raisonnable.

Tant que javascript est "mono-thread" en termes d'exécution de code (à l'exclusion des web-workers et autres), cela se produira toujours. La nature à un seul thread est une énorme simplification dans la plupart des cas, mais nécessite l'idiome non bloquant pour réussir.

Essayez ce code soit dans votre navigateur, soit dans node, et vous verrez qu'il n'y a aucune garantie de précision, au contraire, le setTimeout sera très tardif:

var start = Date.now();

// expecting something close to 500
setTimeout(function(){ console.log(Date.now() - start); }, 500);

// fiddle with the number of iterations depending on how quick your machine is
for(var i=0; i<5000000; ++i){}

À moins que l'interpréteur n'optimise la boucle (ce qu'il ne fait pas sur chrome), vous obtiendrez quelque chose dans les milliers. Retirez la boucle et vous verrez que c'est 500 sur le nez ...

Davin
la source
9

La seule façon de s'assurer que le code est exécuté est de placer votre logique setTimeout dans un processus différent.

Utilisez le module de processus enfant pour générer un nouveau programme node.js qui fait votre logique et transmet les données à ce processus via une sorte de flux (peut-être tcp).

De cette façon, même si un long code de blocage est en cours d'exécution dans votre processus principal, votre processus enfant a déjà démarré et placé un setTimeout dans un nouveau processus et un nouveau thread et s'exécutera donc lorsque vous vous y attendez.

Une complication supplémentaire se situe au niveau du matériel où vous avez plus de threads en cours d'exécution que de processus et donc le changement de contexte entraînera des retards (très mineurs) par rapport à votre timing attendu. Cela devrait être négligeable et si cela est important, vous devez sérieusement réfléchir à ce que vous essayez de faire, pourquoi vous avez besoin d'une telle précision et quel type de matériel alternatif en temps réel est disponible pour faire le travail à la place.

En général, l'utilisation de processus enfants et l'exécution de plusieurs applications de nœuds en tant que processus séparés avec un équilibreur de charge ou un stockage de données partagé (comme redis) est important pour faire évoluer votre code.

Raynos
la source
9

setTimeoutest une sorte de Thread , il tient une opération pendant un temps donné et s'exécute.

setTimeout(function,time_in_mills);

ici, le premier argument doit être un type de fonction; à titre d'exemple, si vous souhaitez imprimer votre nom après 3 secondes, votre code devrait être quelque chose comme ci-dessous.

setTimeout(function(){console.log('your name')},3000);

Le point clé à retenir est que ce que vous voulez faire en utilisant la setTimeoutméthode, faites-le dans une fonction . Si vous souhaitez appeler une autre méthode en analysant certains paramètres, votre code devrait ressembler à ci-dessous:

setTimeout(function(){yourOtherMethod(parameter);},3000);
Ashana.Jackol
la source
8

setTimeout(callback,t)est utilisé pour exécuter un rappel après au moins t milliseconde . Le délai réel dépend de nombreux facteurs externes tels que la granularité du minuteur du système d'exploitation et la charge du système.

Il est donc possible qu'il soit appelé légèrement après l'heure définie, mais qu'il ne soit jamais appelé avant.

Une minuterie ne peut pas durer plus de 24,8 jours.

Siva Prakash
la source