web-dev-qa-db-fra.com

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

J'imagine qu'une fois exécuté, il se trouve dans la file d'attente, mais y a-t-il une garantie qu'il sera invoqué exactement après X millisecondes? Ou d'autres tâches lourdes situées plus haut dans la file d'attente le retarderont-elles?

106
MyCodeGone

La sémantique de setTimeout est sensiblement la même que dans un navigateur Web: le délai d'attente arg est un nombre minimum de ms à attendre avant de s'exécuter, pas une garantie. En outre, si vous passez 0, un nombre non numérique ou un nombre négatif le fera attendre un nombre minimal de ms. Dans Node, cela représente 1 ms, mais dans les navigateurs, il peut atteindre 50 ms.

La raison en est qu'il n'y a pas de préemption de JavaScript par JavaScript. Considérons 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'attente pour 100ms.
  2. occupé pendant 5000ms.
  3. revenir à la boucle d'événement. vérifier les timers en attente et exécuter.

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

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

L'exécution JavaScript de Node à un seul thread rend son travail beaucoup plus simple que la plupart des autres styles de concurrence. Bien sûr, le compromis est qu’il est possible pour une partie du programme mal conduite 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.

160
isaacs

L'idée de non-blocage est que les itérations de boucle sont rapides. Donc, itérer pour chaque tick devrait prendre assez de temps pour que setTimeout soit précis avec une précision raisonnable (au moins environ 100 ms).

En théorie, vous avez raison. Si j'écris une application et bloque la tique, alors setTimeouts sera retardé. Donc, pour répondre à votre question, qui peut s'assurer que setTimeouts est exécuté à temps? En écrivant du code non bloquant, vous pouvez contrôler le degré de précision jusqu’à un degré de précision raisonnable.

Tant que javascript est "single-threaded" en termes d'exécution de code (à l'exclusion des travailleurs Web et autres), cela arrivera 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 dans votre navigateur ou dans un nœud, et vous verrez qu'il n'y a aucune garantie de précision, au contraire, 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ète n'optimise la boucle (ce qui n'est pas le cas avec Chrome), vous obtiendrez quelque chose par milliers. Enlevez la boucle et vous verrez qu'il en a 500 au nez ...

41
davin

Le seul moyen de vous 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 créer un nouveau programme node.js qui gère votre logique et transmet les données à ce processus via un flux (éventuellement tcp).

Ainsi, même si un long code bloquant est en cours d’exécution dans votre processus principal, votre processus enfant s’est déjà lancé et a placé un setTimeout dans un nouveau processus et un nouveau thread.

Une complication supplémentaire se situe au niveau du matériel où plus de threads sont en cours d’exécution que les processus, de sorte que le changement de contexte provoquera des retards (très mineurs) par rapport à la durée prévue. Cela devrait être négligeable et si cela importe, vous devez envisager sérieusement 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 effectuer le travail à la place.

En général, il est important d’utiliser des processus enfants et d’exécuter des applications à plusieurs noeuds en tant que processus distincts avec un équilibreur de charge ou un stockage de données partagé (tel que redis) pour la mise à l’échelle de votre code.

8
Raynos

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

Ainsi, il est possible que l'appel soit appelé légèrement après l'heure définie, mais qu'il ne soit jamais appelé auparavant.

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

6
Siva Prakash

setTimeout est une sorte de Thread, il conserve une opération pendant un temps donné et s'exécute.

setTimeout(function,time_in_mills);

ici, le premier argument devrait être un type de fonction. Par exemple, si vous souhaitez imprimer votre nom après 3 secondes, votre code doit ressembler à ce qui suit.

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

la clé à retenir est ce que vous voulez faire en utilisant la méthode setTimeout, faites-le dans une fonction. si vous voulez appeler une autre méthode en regroupant certains paramètres, votre code devrait ressembler à celui ci-dessous,

setTimeout(function(){yourOtherMethod(parameter);},3000);

noeud de codage heureux :)

6
Ashana.Jackol