setImmediate vs nextTick


336

La version 0.10 de Node.js a été publiée aujourd'hui et introduite setImmediate. La documentation des modifications de l'API suggère de l'utiliser lors des nextTickappels récursifs .

D'après ce que dit MDN, cela semble très similaire process.nextTick.

Quand dois-je utiliser nextTicket quand dois-je utiliser setImmediate?


20
Il y a 5 paragraphes sur ce changement sur le blog blog.nodejs.org/2013/03/11/node-v0-10-0-stable
mak

1
D'après les repères de performances, il semble nextTickplus rapide que setImmediatesur les grands calculs.

10
Pour la petite histoire, j'ai lu ces cinq paragraphes en premier et j'ai quand même fini sur cette question alors que cela ne clarifiait rien pour moi. La réponse acceptée est beaucoup plus concise et décrit en fait ce qui setImmediatefait plus en détail.
Chev

J'ai expliqué la différence en détail dans mon blog .
plafer

Est-il possible que GC puisse fonctionner avant setImmediate, mais pas avant nextTick?

Réponses:


511

À utiliser setImmediatesi vous souhaitez mettre la fonction en file d'attente derrière les rappels d'événements d'E / S qui se trouvent déjà dans la file d'attente des événements. Permet process.nextTickde mettre en file d'attente effective la fonction en tête de la file d'attente des événements afin qu'elle s'exécute immédiatement après la fin de la fonction en cours.

Donc, dans le cas où vous essayez de rompre un travail long et lié au processeur à l'aide de la récursivité, vous voudriez maintenant utiliser setImmediateplutôt que de process.nextTickmettre en file d'attente la prochaine itération, sinon les rappels d'événements d'E / S n'auraient pas la chance pour s'exécuter entre les itérations.


86
Les rappels passés à process.nextTick seront généralement appelés à la fin du flux d'exécution en cours et sont donc à peu près aussi rapides que d'appeler une fonction de manière synchrone. Si cette case n'est pas cochée, cela affamerait la boucle d'événements, empêchant toute E / S de se produire. setImmediates sont mis en file d'attente dans l'ordre créé et sont extraits de la file d'attente une fois par itération de boucle. Ceci est différent de process.nextTick qui exécutera les rappels mis en file d'attente process.maxTickDepth par itération. setImmediate cédera à la boucle d'événements après avoir déclenché un rappel en file d'attente pour s'assurer que les E / S ne sont pas affamées.
Benjamin Gruenbaum

2
@UstamanSangat setImmediate est uniquement pris en charge par IE10 +, tous les autres navigateurs refusent obstinément de mettre en œuvre la future norme probable car ils n'aiment pas être battus par Microsoft. Pour obtenir un résultat similaire dans FF / Chrome, vous pouvez utiliser postMessage (publier un message dans votre propre fenêtre). Vous pouvez également utiliser requestAnimationFrame, surtout si vos mises à jour sont liées à l'interface utilisateur. setTimeout (func, 0) ne fonctionne pas du tout comme process.nextTick.
fabspro

45
@fabspro "parce qu'ils n'aiment pas être battus mon Microsoft" vous fait mal à propos de quelque chose. C'est surtout parce que c'est terriblement, terriblement nommé. S'il n'y a qu'une seule fois la fonction setImmediate ne fonctionnera jamais, c'est immédiatement. Le nom de la fonction est exactement l'opposé de ce qu'elle fait. nextTick et setImmediate feraient mieux de basculer; setImmediate s'exécute immédiatement après la fin de la pile actuelle (avant d'attendre les E / S) et nextTick s'exécute à la fin du tick suivant (après avoir attendu les E / S). Mais cela a déjà été dit mille fois.
Craig Andrews

4
@fabspro Mais malheureusement, la fonction s'appelle nextTick. nextTick s'exécute "immédiatement" tandis que setImmediate ressemble plus à un setTimeout / postMessage.
Robert

1
@CraigAndrews J'éviterais requestAnimationFramecar cela ne se produit pas toujours (j'ai certainement vu cela, je pense que l'exemple n'était pas l'onglet actuel) et il peut être appelé avant que la page ait terminé la peinture (c'est-à-dire que le navigateur est toujours occupé à dessiner).
robocat

68

Pour illustrer

import fs from 'fs';
import http from 'http';

const options = {
  host: 'www.stackoverflow.com',
  port: 80,
  path: '/index.html'
};

describe('deferredExecution', () => {
  it('deferredExecution', (done) => {
    console.log('Start');
    setTimeout(() => console.log('TO1'), 0);
    setImmediate(() => console.log('IM1'));
    process.nextTick(() => console.log('NT1'));
    setImmediate(() => console.log('IM2'));
    process.nextTick(() => console.log('NT2'));
    http.get(options, () => console.log('IO1'));
    fs.readdir(process.cwd(), () => console.log('IO2'));
    setImmediate(() => console.log('IM3'));
    process.nextTick(() => console.log('NT3'));
    setImmediate(() => console.log('IM4'));
    fs.readdir(process.cwd(), () => console.log('IO3'));
    console.log('Done');
    setTimeout(done, 1500);
  });
});

donnera la sortie suivante

Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1

J'espère que cela peut aider à comprendre la différence.

Actualisé:

Les rappels sont différés avec l' process.nextTick()exécution avant le déclenchement de tout autre événement d'E / S, tandis qu'avec setImmediate (), l'exécution est mise en file d'attente derrière tout événement d'E / S qui se trouve déjà dans la file d'attente.

Node.js Design Patterns , par Mario Casciaro (probablement le meilleur livre sur node.js / js)


2
C'est vraiment utile merci. Je pense que les images et les exemples sont le moyen le plus rapide de comprendre quelque chose.
John James

1
Je pense qu'il est important de souligner que setTimeout () et setImmediate () lorsqu'ils ne sont pas dans un cycle d'E / S, l'ordre n'est pas déterministe en fonction des performances d'un processus. nodejs.org/en/docs/guides/event-loop-timers-and-nexttick For example, if we run the following script which is not within an I/O cycle (i.e. the main module), the order in which the two timers are executed is non-deterministic, as it is bound by the performance of the process: Donc, cette réponse ne répond pas vraiment à la différence exacte mais simplement un exemple qui peut varier dans un contexte différent
Actung

Comme l'a souligné @Actung. il est très important de savoir si setTimetout et setImmediate se trouvent ou non dans un cycle d'E / S, pour déterminer le résultat.
Rajika Imal

50

Je pense que je peux illustrer cela très bien. Puisque nextTickest appelé à la fin de l'opération en cours, l'appel récursif peut empêcher la boucle d'événement de continuer. setImmediaterésout cela en tirant dans la phase de vérification de la boucle d'événements, permettant à la boucle d'événements de continuer normalement.

   ┌───────────────────────┐
┌─>│        timers         
  └──────────┬────────────┘
  ┌──────────┴────────────┐
       I/O callbacks     
  └──────────┬────────────┘
  ┌──────────┴────────────┐
       idle, prepare     
  └──────────┬────────────┘      ┌───────────────┐
  ┌──────────┴────────────┐         incoming:   
           poll          │<─────┤  connections, 
  └──────────┬────────────┘         data, etc.  
  ┌──────────┴────────────┐      └───────────────┘
          check          
  └──────────┬────────────┘
  ┌──────────┴────────────┐
└──┤    close callbacks    
   └───────────────────────┘

source: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Notez que la phase de vérification est immédiatement après la phase d'interrogation. En effet, la phase d'interrogation et les rappels d'E / S sont les endroits les plus susceptibles setImmediatede s'exécuter. Donc, idéalement, la plupart de ces appels seront en fait assez immédiats, mais pas aussi immédiats que nextTickce qui est vérifié après chaque opération et existe techniquement en dehors de la boucle d'événements.

Jetons un coup d'œil à un petit exemple de la différence entre setImmediateet process.nextTick:

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from setImmediate handler.
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
  });
}
step(0);

Supposons que nous venons d'exécuter ce programme et que nous procédions à la première itération de la boucle d'événements. Il appellera la stepfonction avec l'itération zéro. Il enregistrera ensuite deux gestionnaires, un pour setImmediateet un pour process.nextTick. Nous appelons ensuite récursivement cette fonction à partir du setImmediategestionnaire qui s'exécutera dans la prochaine phase de vérification. Le nextTickgestionnaire s'exécutera à la fin de l'opération en cours interrompant la boucle d'événements, donc même s'il a été enregistré en second, il s'exécutera en premier.

L'ordre finit par être: se nextTickdéclenche à la fin de l'opération en cours, la prochaine boucle d'événement commence, les phases normales de la boucle d'événement s'exécutent, se setImmediatedéclenche et appelle récursivement notre stepfonction pour recommencer le processus. L'opération en cours se termine, se nextTickdéclenche, etc.

La sortie du code ci-dessus serait:

nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9

Passons maintenant à notre appel récursif stepdans notre nextTickgestionnaire au lieu de setImmediate.

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from nextTick handler.
  });
}
step(0);

Maintenant que nous avons déplacé l'appel récursif stepdans le nextTickgestionnaire, les choses se comporteront dans un ordre différent. Notre première itération de la boucle d'événements s'exécute et appelle l' stepenregistrement d'un setImmedaitegestionnaire ainsi que d'un nextTickgestionnaire. Une fois l'opération en cours terminée, notre nextTickgestionnaire se déclenche stepet appelle et enregistre récursivement un autre setImmediategestionnaire ainsi qu'un autre nextTickgestionnaire. Étant donné qu'un nextTickgestionnaire se déclenche après l'opération en cours, l'inscription d'un nextTickgestionnaire dans un nextTickgestionnaire entraînera l'exécution du deuxième gestionnaire immédiatement après la fin de l'opération en cours. Les nextTickgestionnaires continueront de tirer, empêchant la boucle d'événement actuelle de se poursuivre. Nous traverserons tous nosnextTickavant de voir un seul setImmediateincendie de gestionnaire.

La sortie du code ci-dessus finit par être:

nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9

Notez que si nous n'avions pas interrompu l'appel récursif et l'avions abandonné après 10 itérations, les nextTickappels continueraient de se reproduire et ne laisseraient jamais la boucle d'événement passer à la phase suivante. C'est ainsi que le nextTickblocage peut se produire lorsqu'il est utilisé de manière récursive alors qu'il setImmediatese déclenche dans la prochaine boucle d'événements et la définition d'un autre setImmediategestionnaire à l'intérieur de celui-ci n'interrompt pas du tout la boucle d'événements actuelle, ce qui lui permet de continuer à exécuter les phases de la boucle d'événements comme d'habitude.

J'espère que cela pourra aider!

PS - Je suis d'accord avec d'autres commentateurs sur le fait que les noms des deux fonctions pourraient facilement être échangés car nextTickon dirait que ça va se déclencher dans la prochaine boucle d'événement plutôt que la fin de la boucle actuelle, et la fin de la boucle actuelle est plus "immédiate" "que le début de la boucle suivante. Eh bien, c'est ce que nous obtenons à mesure qu'une API arrive à maturité et que les gens dépendent des interfaces existantes.


2
Description assez claire. Je pense que cette réponse a besoin de plus de votes positifs.
Actung

Bien expliqué (Y)
Dhiraj Sharma

il est important de réitérer l'avertissement de Node concernant l'utilisation de process.nextTick. Si vous mettez en file d'attente un grand nombre de rappels dans le nextTickQueue, vous pouvez potentiellement affamer la boucle d'événements en vous assurant que la phase d'interrogation n'est jamais atteinte. C'est la raison pour laquelle vous devriez généralement préférer setImmediate.
faridcs

1
Merci, c'était la meilleure explication. l'exemple de code a vraiment aidé.
skyhavoc

@skyhavoc heureux d'avoir pu aider!
Chev

30

Dans les commentaires de la réponse, il n'est pas indiqué explicitement que nextTick est passé de la macrosémantique à la microsémantique.

avant le nœud 0.9 (lorsque setImmediate a été introduit), nextTick fonctionnait au début de la prochaine pile d'appels.

depuis le noeud 0.9, nextTick fonctionne à la fin de la pile d'appels existante, tandis que setImmediate est au début de la pile d'appels suivante

consultez https://github.com/YuzuJS/setImmediate pour les outils et les détails


11

En termes simples, process.NextTick () serait exécuté au prochain tick de la boucle d'événement. Cependant, setImmediate a essentiellement une phase distincte qui garantit que le rappel enregistré sous setImmediate () ne sera appelé qu'après la phase de rappel et d'interrogation IO.

Veuillez vous référer à ce lien pour une belle explication: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -it-metrics-c4907b19da4c

événements de boucle d'événements simplifiés


8

Quelques bonnes réponses ici détaillant comment ils fonctionnent tous les deux.

Il suffit d'en ajouter un qui répond à la question spécifique posée:

Quand dois-je utiliser nextTicket quand dois-je utiliser setImmediate?


Utilisez toujours setImmediate.


La boucle d'événement Node.js, les minuteurs et laprocess.nextTick() doc comprennent les éléments suivants:

Nous recommandons aux développeurs d'utiliser setImmediate()dans tous les cas, car il est plus facile de raisonner (et cela conduit à un code compatible avec une plus grande variété d'environnements, comme le navigateur JS.)


Plus tôt dans le document, il avertit que cela process.nextTickpeut conduire à ...

quelques mauvaises situations car cela vous permet de "affamer" vos E / S en effectuant des process.nextTick()appels récursifs , ce qui empêche la boucle d'événement d'atteindre la phase d' interrogation .

En fait, il process.nextTickpeut même mourir de faim Promises:

Promise.resolve().then(() => { console.log('this happens LAST'); });

process.nextTick(() => {
  console.log('all of these...');
  process.nextTick(() => {
    console.log('...happen before...');
    process.nextTick(() => {
      console.log('...the Promise ever...');
      process.nextTick(() => {
        console.log('...has a chance to resolve');
      })
    })
  })
})

D'autre part, setImmediateest " plus facile à raisonner " et évite ces types de problèmes:

Promise.resolve().then(() => { console.log('this happens FIRST'); });

setImmediate(() => {
  console.log('this happens LAST');
})

Donc, à moins qu'il n'y ait un besoin spécifique pour le comportement unique de process.nextTick, l'approche recommandée consiste à " utiliser setImmediate()dans tous les cas ".


1

Je vous recommande de consulter la section docs dédiée à Loop pour une meilleure compréhension. Un extrait extrait de là:

Nous avons deux appels qui sont similaires en ce qui concerne les utilisateurs, mais leurs noms prêtent à confusion.

  • process.nextTick () se déclenche immédiatement sur la même phase

  • setImmediate () se déclenche à l'itération ou au «tick» suivant de la
    boucle d'événements

En substance, les noms doivent être échangés. process.nextTick () se déclenche plus immédiatement que setImmediate (), mais il s'agit d'un artefact du passé qui est peu susceptible de changer.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.