setImmediate vs. nextTick

336

La versión 0.10 de Node.js se lanzó hoy y se presentó setImmediate. La documentación de cambios de API sugiere su uso al hacer nextTickllamadas recursivas .

Por lo que dice MDN , parece muy similar a process.nextTick.

¿Cuándo debo usar nextTicky cuándo debo usar setImmediate?

Benjamin Gruenbaum
fuente
20
Hay 5 párrafos acerca de este cambio en el blog blog.nodejs.org/2013/03/11/node-v0-10-0-stable
MAK
1
Según los puntos de referencia de rendimiento, parece que nextTickes más rápido que setImmediateen grandes cálculos.
10
Para el registro, leí esos cinco párrafos primero y todavía terminé con esta pregunta cuando realmente no me aclaraba nada. La respuesta aceptada es mucho más concisa y en realidad describe lo que setImmediatehace con mayor detalle.
Chev
He explicado la diferencia con gran detalle en mi blog .
plafer
¿Es el caso que GC puede ejecutarse antes setImmediate, pero no antes nextTick?

Respuestas:

511

Úselo setImmediatesi desea poner en cola la función detrás de las devoluciones de llamadas de eventos de E / S que ya están en la cola de eventos. Utilícelo process.nextTickpara poner en cola efectivamente la función en la cabecera de la cola de eventos para que se ejecute inmediatamente después de que se complete la función actual.

Por lo tanto, en un caso en el que intente romper un trabajo de larga duración vinculado a la CPU utilizando la recursividad, ahora querrá usar en setImmediatelugar de process.nextTickponer en cola la próxima iteración, de lo contrario, cualquier devolución de llamada de eventos de E / S no tendría la oportunidad correr entre iteraciones.

JohnnyHK
fuente
86
Las devoluciones de llamada pasadas a process.nextTick generalmente se llamarán al final del flujo de ejecución actual y, por lo tanto, son aproximadamente tan rápidas como llamar a una función sincrónicamente. Si no se controla, esto privaría al bucle de eventos, evitando que ocurra cualquier E / S. setImmediates se ponen en cola en el orden creado y se sacan de la cola una vez por iteración de bucle. Esto es diferente de process.nextTick que ejecutará las devoluciones de llamada en cola process.maxTickDepth por iteración. setImmediate cederá al bucle de eventos después de disparar una devolución de llamada en cola para asegurarse de que la E / S no se esté eliminando.
Benjamin Gruenbaum
2
@UstamanSangat setImmediate solo es compatible con IE10 +, todos los demás navegadores se niegan obstinadamente a implementar el probable estándar futuro porque no les gusta que Microsoft los golpee. Para lograr un resultado similar en FF / Chrome, puede usar postMessage (publicar un mensaje en su propia ventana). También podría considerar el uso de requestAnimationFrame, especialmente si sus actualizaciones están relacionadas con la interfaz de usuario. setTimeout (func, 0) no funciona como process.nextTick en absoluto.
fabspro
45
@fabspro "porque no les gusta que me golpeen mi Microsoft" te hace sentir molesto por algo. Es principalmente porque tiene un nombre terrible, terrible. Si hay una sola vez, la función setImmediate nunca se ejecutará, es inmediatamente. El nombre de la función es exactamente lo contrario de lo que hace. nextTick y setImmediate sería mejor cambiar; setImmediate se ejecuta inmediatamente después de que se completa la pila actual (antes de esperar E / S) y nextTick se ejecuta al final del siguiente tick (después de esperar E / S). Pero entonces, esto ya se ha dicho mil veces.
Craig Andrews
44
@fabspro Pero desafortunadamente la función se llama nextTick. nextTick se ejecuta "inmediatamente" mientras setImmediate se parece más a setTimeout / postMessage.
Robert
1
@CraigAndrews Evitaría requestAnimationFrameporque no siempre ocurre (definitivamente he visto esto, creo que el ejemplo era la pestaña no era la pestaña actual) y se puede llamar antes de que la página haya completado la pintura (es decir, el navegador todavía está ocupado dibujando).
robocat
68

Como una ilustracion

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);
  });
});

dará el siguiente resultado

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

Espero que esto pueda ayudar a entender la diferencia.

Actualizado:

Las devoluciones de llamada diferidas con la process.nextTick()ejecución antes de que se active cualquier otro evento de E / S, mientras que con setImmediate (), la ejecución se pone en cola detrás de cualquier evento de E / S que ya esté en la cola.

Patrones de diseño de Node.js , por Mario Casciaro (probablemente el mejor libro sobre node.js / js)

DraganS
fuente
2
Esto es realmente útil, gracias. Creo que las imágenes y los ejemplos son la forma más rápida de entender algo.
John James
1
Creo que es importante señalar que setTimeout () y setImmediate () cuando no están dentro de un ciclo de E / S, el orden no es determinista, dependiendo del rendimiento de un proceso. 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: Entonces, esta respuesta realmente no responde a la diferencia exacta, sino simplemente un ejemplo que podría variar en un contexto diferente
Actung
Como señaló @Actung. Es muy importante saber si setTimetout y setImmediate están dentro de un ciclo de E / S o no, para determinar el resultado.
Rajika Imal
50

Creo que puedo ilustrar esto bastante bien. Como nextTickse llama al final de la operación actual, llamarla de forma recursiva puede terminar bloqueando la continuación del bucle de eventos. setImmediateresuelve esto disparando en la fase de verificación del bucle de eventos, permitiendo que el bucle de eventos continúe normalmente.

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

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

Observe que la fase de verificación es inmediatamente posterior a la fase de sondeo. Esto se debe a que la fase de sondeo y las devoluciones de llamadas de E / S son los lugares más probables en los que setImmediatese ejecutarán sus llamadas . Así que, idealmente, la mayoría de esas llamadas serán bastante inmediatas, pero no tan inmediatas como las nextTickque se verifican después de cada operación y técnicamente existen fuera del bucle de eventos.

Echemos un vistazo a un pequeño ejemplo de la diferencia entre setImmediatey 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);

Digamos que acabamos de ejecutar este programa y estamos pasando por la primera iteración del bucle de eventos. Llamará a la stepfunción con la iteración cero. Luego registrará dos controladores, uno para setImmediatey otro para process.nextTick. Luego llamamos recursivamente a esta función desde el setImmediatecontrolador que se ejecutará en la siguiente fase de verificación. El nextTickcontrolador se ejecutará al final de la operación actual interrumpiendo el bucle de eventos, por lo que, aunque se registró en segundo lugar, se ejecutará primero.

El orden termina siendo: nextTickdispara a medida que finaliza la operación actual, comienza el siguiente bucle de eventos, se ejecutan las fases normales del bucle de eventos, se setImmediatedispara y recursivamente llama a nuestra stepfunción para comenzar el proceso nuevamente. Finaliza la operación actual, nextTickincendios, etc.

La salida del código anterior sería:

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

Ahora pasemos nuestra llamada recursiva a stepnuestro nextTickcontrolador en lugar del 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);

Ahora que se ha pasado la llamada recursiva a stepen las nextTickcosas del manejador se comportará en un orden diferente. Nuestra primera iteración del bucle de eventos se ejecuta y llama al stepregistro de un setImmedaitecontrolador y un nextTickcontrolador. Después de que finaliza la operación actual, nuestro nextTickcontrolador se activa stepy llama y registra recursivamente a otro setImmediatecontrolador, así como a otro nextTickcontrolador. Dado que un nextTickcontrolador se dispara después de la operación actual, registrar un nextTickcontrolador dentro de un nextTickcontrolador hará que el segundo controlador se ejecute inmediatamente después de que finalice la operación del controlador actual. Los nextTickcontroladores seguirán disparando, evitando que el ciclo de eventos actual continúe. Vamos a superar todos nuestrosnextTickmanejadores antes de que veamos un solo setImmediatemanejo de fuego.

La salida del código anterior termina siendo:

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

Tenga en cuenta que si no hubiéramos interrumpido la llamada recursiva y la hubiéramos abortado después de 10 iteraciones, las nextTickllamadas seguirían recurriendo y nunca dejarían que el bucle de eventos continuara a la siguiente fase. Así es como nextTickpuede convertirse en bloqueo cuando se usa de forma recursiva, mientras que setImmediatese disparará en el próximo ciclo de eventos y establecer otro setImmediatecontrolador desde dentro no interrumpirá en absoluto el ciclo de eventos actual, lo que le permitirá continuar ejecutando fases del ciclo de eventos de manera normal.

¡Espero que ayude!

PD: estoy de acuerdo con otros comentaristas en que los nombres de las dos funciones podrían intercambiarse fácilmente, ya que nextTickparece que se activará en el siguiente bucle de eventos en lugar del final del actual, y el final del bucle actual es más "inmediato" "que el comienzo del siguiente ciclo. Bueno, eso es lo que obtenemos a medida que una API madura y la gente depende de las interfaces existentes.

Chev
fuente
2
Descripción bastante clara. Creo que esta respuesta necesita más votos a favor.
Actung
Bien explicado (Y)
Dhiraj Sharma
Es importante reiterar la advertencia de Node sobre el uso de process.nextTick. Si pone en cola una gran cantidad de devoluciones de llamada en el nextTickQueue, puede potencialmente privar al bucle de eventos asegurándose de que nunca se llegue a la fase de sondeo. Esta es la razón por la que generalmente debería preferir setImmediate.
faridcs
1
Gracias, esta fue la mejor explicación. el código de muestra realmente ayudó.
skyhavoc
@skyhavoc me alegro de poder ayudar!
Chev
30

En los comentarios en la respuesta, no indica explícitamente que nextTick cambió de Macrosemantics a Microsemantics.

antes del nodo 0.9 (cuando se introdujo setImmediate), nextTick funcionó al comienzo de la siguiente pila de llamadas.

desde el nodo 0.9, nextTick funciona al final de la pila de llamadas existente, mientras que setImmediate está al comienzo de la siguiente pila de llamadas

echa un vistazo a https://github.com/YuzuJS/setImmediate para obtener herramientas y detalles

Jay Day Zee
fuente
11

En términos simples, process.NextTick () se ejecutaría en el siguiente tic del bucle de eventos. Sin embargo, setImmediate, básicamente, tiene una fase separada que garantiza que la devolución de llamada registrada bajo setImmediate () se invoque solo después de la fase de devolución de llamada y sondeo de E / S.

Consulte este enlace para obtener una buena explicación: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -its-metrics-c4907b19da4c

eventos de bucle de eventos simplificados

Cerveza de jengibre
fuente
8

Aquí hay algunas respuestas excelentes que detallan cómo funcionan ambas.

Simplemente agregue uno que responda a la pregunta específica que se hace:

¿Cuándo debo usar nextTicky cuándo debo usar setImmediate?


Siempre uso setImmediate.


El Node.js Event Loop, Timers yprocess.nextTick() doc incluyen lo siguiente:

Recomendamos que los desarrolladores lo usen setImmediate()en todos los casos porque es más fácil razonar sobre ello (y conduce a un código que es compatible con una variedad más amplia de entornos, como el navegador JS).


Anteriormente en el documento advierte que process.nextTickpuede conducir a ...

algunas situaciones malas porque le permite "matar de hambre" su E / S al hacer process.nextTick()llamadas recursivas , lo que evita que el bucle de eventos llegue a la fase de sondeo .

Como resultado, process.nextTickincluso puede morir de hambre 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');
      })
    })
  })
})

Por otro lado, setImmediatees " más fácil razonar " y evita este tipo de problemas:

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

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

Por lo tanto, a menos que exista una necesidad específica del comportamiento único de process.nextTick, el enfoque recomendado es " utilizarlo setImmediate()en todos los casos ".

Brian Adams
fuente
1

Le recomiendo que consulte la sección de documentos dedicada a Loop para obtener una mejor comprensión. Algunos fragmentos tomados de allí:

Tenemos dos llamadas que son similares en lo que respecta a los usuarios, pero sus nombres son confusos.

  • process.nextTick () se dispara inmediatamente en la misma fase

  • setImmediate () se activa en la siguiente iteración o 'tic' del
    bucle de eventos

En esencia, los nombres deben intercambiarse. process.nextTick () se dispara más inmediatamente que setImmediate (), pero este es un artefacto del pasado que es poco probable que cambie.

Valikhan Akhmedov
fuente