setImmediate vs. nextTick

336

A versão 0.10 do Node.js. foi lançada hoje e apresentada setImmediate. A documentação de alterações da API sugere usá-lo ao fazer nextTickchamadas recursivas .

Pelo que a MDN diz , parece muito semelhante process.nextTick.

Quando devo usar nextTicke quando devo usar setImmediate?

Benjamin Gruenbaum
fonte
20
Há 5 parágrafos sobre essa mudança no blog blog.nodejs.org/2013/03/11/node-v0-10-0-stable
mak
11
A partir dos benchmarks de desempenho, parece nextTickmais rápido do que setImmediateem grandes cálculos.
10
Para constar, li esses cinco parágrafos primeiro e ainda acabei com essa pergunta quando realmente não esclareceu nada para mim. A resposta aceita é muito mais concisa e na verdade descreve o que setImmediatefaz com mais detalhes.
Chev
Eu expliquei a diferença em grandes detalhes no meu blog .
plafer 25/09/15
É o caso que o GC pode ser executado antes setImmediate, mas não antes nextTick?

Respostas:

510

Use setImmediatese você deseja enfileirar a função atrás de qualquer retorno de chamada de evento de E / S que já esteja na fila de eventos. Use process.nextTickpara enfileirar efetivamente a função no início da fila de eventos, para que ela seja executada imediatamente após a conclusão da função atual.

Portanto, em um caso em que você está tentando interromper um trabalho de longa duração, vinculado à CPU usando recursão, agora deseja usar, em setImmediatevez de process.nextTickenfileirar a próxima iteração, caso contrário, qualquer retorno de chamada de evento de E / S não teria a chance para executar entre iterações.

JohnnyHK
fonte
86
Os retornos de chamada passados ​​para process.nextTick geralmente serão chamados no final do fluxo atual de execução e, portanto, são aproximadamente tão rápidos quanto chamar uma função de forma síncrona. Deixado desmarcado, isso faria com que o loop de eventos morresse de fome, impedindo a ocorrência de qualquer E / S. setImmediates são enfileirados na ordem criada e são retirados da fila uma vez por iteração de loop. Isso é diferente de process.nextTick, que executará retornos de chamada em fila process.maxTickDepth por iteração. setImmediate renderá ao loop de eventos após disparar um retorno de chamada na fila para garantir que a E / S não esteja passando fome.
Benjamin Gruenbaum
2
O @UstamanSangat setImmediate é suportado apenas pelo IE10 +, todos os outros navegadores se recusam teimosamente a implementar o provável padrão futuro, porque não gostam de ser derrotados pela Microsoft. Para obter um resultado semelhante no FF / Chrome, você pode usar o postMessage (poste uma mensagem na sua própria janela). Você também pode usar requestAnimationFrame, principalmente se suas atualizações estiverem relacionadas à interface do usuário. setTimeout (func, 0) não funciona como process.nextTick.
Fabspro #
44
@fabspro "porque eles não gostam de ser derrotados na minha Microsoft" faz você parecer dolorido com alguma coisa. É principalmente porque tem um nome terrível. Se houver uma vez que a função setImmediate nunca será executada, será imediatamente. O nome da função é exatamente o oposto do que ela faz. nextTick e setImmediate seria melhor alternar; setImmediate é executado imediatamente após a pilha atual ser concluída (antes de aguardar a E / S) e o nextTick é executado no final do próximo tick (após a espera da E / S). Mas então, isso já foi dito milhares de vezes.
Craig Andrews
4
@fabspro Mas infelizmente a função é chamada nextTick. nextTick executa "imediatamente", enquanto setImmediate é mais como um setTimeout / postMessage.
Robert
11
@CraigAndrews Eu evitaria requestAnimationFrameporque nem sempre ocorre (eu definitivamente vi isso, acho que o exemplo era guia não era guia atual) e pode ser chamado antes que a página conclua a pintura (ou seja, o navegador ainda está ocupado desenhando).
robocat 19/03/14
68

Como uma ilustração

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á a seguinte saída

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

Espero que isso ajude a entender a diferença.

Atualizada:

process.nextTick()Retornos de chamada adiados com execução antes de qualquer outro evento de E / S ser acionado, enquanto que com setImmediate (), a execução é enfileirada atrás de qualquer evento de E / S que já esteja na fila.

Node.js. Design Patterns , de Mario Casciaro (provavelmente o melhor livro sobre o node.js./js)

DraganS
fonte
2
Isso é realmente útil, obrigado. Acho que imagens e exemplos são a maneira mais rápida de entender alguma coisa.
John James
11
Acho importante ressaltar que setTimeout () e setImmediate () quando não estão em um ciclo de E / S, a ordem não é determinística, dependendo do desempenho de um processo. 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: Portanto, esta resposta não responde exatamente a diferença exata, mas apenas um exemplo que pode variar em diferentes contextos
Actung
Como apontado por @Actung. é muito importante saber se setTimetout e setImmediate estão dentro de um ciclo de E / S ou não, para determinar o resultado.
Rajika Imal
50

Eu acho que posso ilustrar isso muito bem. Como nextTické chamado no final da operação atual, chamá-lo recursivamente pode acabar impedindo a continuidade do loop de eventos. setImmediateresolve isso disparando na fase de verificação do loop de eventos, permitindo que o loop de eventos continue normalmente.

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

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

Observe que a fase de verificação ocorre imediatamente após a fase de votação. Isso ocorre porque a fase de pesquisa e os retornos de chamada de E / S são os locais mais prováveis ​​para os quais suas chamadas setImmediateserão executadas. Portanto, idealmente, a maioria dessas chamadas será realmente imediata, mas não tão imediata quanto a nextTickque é verificada após cada operação e existe tecnicamente fora do loop de eventos.

Vamos dar uma olhada em um pequeno exemplo da diferença entre setImmediatee 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 executar este programa e estamos avançando na primeira iteração do loop de eventos. Ele chamará a stepfunção com a iteração zero. Em seguida, ele registrará dois manipuladores, um para setImmediatee outro para process.nextTick. Em seguida, chamamos recursivamente essa função do setImmediatemanipulador que será executado na próxima fase de verificação. O nextTickmanipulador será executado no final da operação atual, interrompendo o loop de eventos; portanto, mesmo que tenha sido registrado em segundo, ele será executado primeiro.

A ordem acaba sendo: nextTickdispara quando a operação atual termina, o próximo loop de evento começa, as fases normais do loop de evento são executadas, setImmediatedispara e chama recursivamente nossa stepfunção para iniciar o processo novamente. A operação atual termina, nextTickdispara, etc.

A saída do código acima seria:

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

Agora vamos passar nossa chamada recursiva para stepnosso nextTickmanipulador, em vez 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);

Agora que transferimos a chamada recursiva para stepo nextTickmanipulador, as coisas se comportarão em uma ordem diferente. Nossa primeira iteração do loop de eventos é executada e chama o stepregistro de um setImmedaitemanipulador e de um nextTickmanipulador. Após o término da operação atual, nosso nextTickmanipulador é acionado, o que chama stepe registra recursivamente outro setImmediatemanipulador e outro nextTickmanipulador. Como um nextTickmanipulador é acionado após a operação atual, o registro de um nextTickmanipulador em um nextTickmanipulador fará com que o segundo manipulador seja executado imediatamente após o término da operação do manipulador atual. Os nextTickmanipuladores continuarão disparando, impedindo que o loop de eventos atual continue. Vamos passar por todo o nossonextTickmanipuladores antes de vermos um único setImmediateacionador disparar.

A saída do código acima acaba sendo:

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

Observe que, se não tivéssemos interrompido a chamada recursiva e a abortado após 10 iterações, as nextTickchamadas continuariam recorrendo e nunca deixando o loop de eventos continuar para a próxima fase. É assim que nextTickpode ser bloqueado quando usado recursivamente, enquanto setImmediatedispara no próximo loop de eventos e definir outro setImmediatemanipulador de dentro de um não interrompe o loop de eventos atual, permitindo que continue executando as fases do loop de eventos normalmente.

Espero que ajude!

PS - Eu concordo com outros comentaristas que os nomes das duas funções poderiam ser facilmente trocados, pois nextTickparece que isso será acionado no próximo loop de eventos, em vez do final do atual, e o final do atual é mais "imediato" "que o início do próximo loop. Bem, é isso que obtemos à medida que a API amadurece e as pessoas dependem das interfaces existentes.

Chev
fonte
2
Descrição bastante clara. Eu acho que essa resposta precisa de mais votos.
Actung
Bem explicado (Y)
Dhiraj Sharma
é importante reiterar o aviso do Node sobre o uso de process.nextTick. Se você enfileirar um grande número de retornos de chamada no nextTickQueue, poderá potencialmente passar fome pelo loop de eventos, garantindo que a fase de pesquisa nunca seja atingida. É por isso que você geralmente prefere setImmediate.
faridcs 13/08/19
11
Obrigado, esta foi a melhor explicação. o código de exemplo realmente ajudou.
skyhavoc 27/02
@skyhavoc feliz por poder ajudar!
Chev
30

Nos comentários da resposta, não afirma explicitamente que o nextTick passou de Macrosemantics para Microsemantics.

antes do nó 0.9 (quando setImmediate foi introduzido), nextTick operava no início da próxima pilha de chamadas.

desde o nó 0.9, nextTick opera no final do pilha de chamadas existente, enquanto setImmediate está no início da próxima pilha de chamadas

confira https://github.com/YuzuJS/setImmediate para obter ferramentas e detalhes

Jay Day Zee
fonte
11

Em termos simples, process.NextTick () seria executado no próximo tick do loop de eventos. No entanto, o setImmediate basicamente possui uma fase separada que garante que o retorno de chamada registrado em setImmediate () seja chamado somente após a fase de retorno de chamada e pesquisa de opinião.

Consulte este link para obter uma boa explicação: 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 loop de eventos simplificados

Cerveja de gengibre
fonte
8

Algumas ótimas respostas aqui detalhando como os dois funcionam.

Basta adicionar um que responda à pergunta específica:

Quando devo usar nextTicke quando devo usar setImmediate?


Sempre use setImmediate.


O loop de eventos, timers eprocess.nextTick() documentos do Node.js. inclui o seguinte:

Recomendamos que os desenvolvedores usem setImmediate()em todos os casos, porque é mais fácil argumentar (e isso leva a códigos compatíveis com uma variedade maior de ambientes, como o JS do navegador).


No início do documento, ele adverte que process.nextTickpode levar a ...

algumas situações ruins porque permite "privar" sua E / S fazendo process.nextTick()chamadas recursivas , o que impede que o loop de eventos atinja a fase de pesquisa .

Como se vê, process.nextTickpode até morrer de fome 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 outro lado, setImmediateé " mais fácil de raciocinar " e evita esses tipos de problemas:

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

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

Portanto, a menos que haja uma necessidade específica do comportamento exclusivo de process.nextTick, a abordagem recomendada é " usar setImmediate()em todos os casos ".

Brian Adams
fonte
1

Eu recomendo que você verifique a seção de documentos dedicada ao Loop para entender melhor. Alguns trechos retirados de lá:

Temos duas chamadas semelhantes para os usuários, mas seus nomes são confusos.

  • process.nextTick () é acionado imediatamente na mesma fase

  • setImmediate () é acionado na seguinte iteração ou 'tick' do
    loop de eventos

Em essência, os nomes devem ser trocados. process.nextTick () é acionado mais imediatamente que setImmediate (), mas é um artefato do passado que dificilmente será alterado.

Valikhan Akhmedov
fonte