Como o setTimeout funciona no Node.JS?

112

Eu acho que depois de executado, ele está na fila, mas na fila há alguma garantia de que ele será chamado exatamente após X milissegundos? Ou outras tarefas pesadas mais altas na fila o atrasarão?

MyCodeGone
fonte
14
Nenhum sistema operacional fora do tempo real possibilitará que haja uma garantia de precisão séria. Todos os tipos de coisas no sistema podem (e irão) atrapalhar o mecanismo do cronômetro.
Pointy

Respostas:

174

A semântica de setTimeout é praticamente a mesma de um navegador da web: o argumento de tempo limite é um número mínimo de ms a esperar antes da execução, não uma garantia. Além disso, passar 0, um não-número ou um número negativo, fará com que espere um número mínimo de ms. No Node, isso é 1 ms, mas em navegadores pode chegar a 50 ms.

A razão para isso é que não há preempção do JavaScript pelo JavaScript. Considere este exemplo:

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')

O fluxo aqui é:

  1. agende o tempo limite para 100 ms.
  2. busywait por 5000ms.
  3. retornar ao loop de eventos. verifique se há temporizadores pendentes e execute.

Se este não fosse o caso, você poderia fazer com que um bit de JavaScript "interrompesse" outro. Teríamos que configurar mutexes e semáforos e outros, para evitar que códigos como este sejam extremamente difíceis de raciocinar sobre:

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

O single-threadedness da execução do JavaScript do Node torna muito mais simples de trabalhar do que a maioria dos outros estilos de simultaneidade. Obviamente, a desvantagem é que é possível que uma parte malcomportada do programa bloqueie a coisa toda com um loop infinito.

Este é um demônio melhor para lutar do que a complexidade da preempção? Depende.

isaacs
fonte
20
Você recebe um +1 pela console.logmensagem excepcionalmente precisa .
Ação judicial de Monica de
Eu gosto de como você colocou TIME em sua string. Muito boa explicação !!!
Alisson
43

A ideia do não-bloqueio é que as iterações do loop sejam rápidas. Portanto, a iteração para cada tick deve levar um tempo curto o suficiente para que o setTimeout seja preciso com uma precisão razoável (talvez <100 ms ou mais).

Em teoria, você está certo. Se eu escrever um aplicativo e bloquear o tique, setTimeouts será atrasado. Então, para responder à sua pergunta, quem pode garantir que setTimeouts seja executado no prazo? Você, ao escrever um código sem bloqueio, pode controlar o grau de precisão até quase qualquer grau razoável de precisão.

Desde que o javascript seja "single-threaded" em termos de execução de código (excluindo web-workers e outros), isso sempre acontecerá. A natureza de thread único é uma grande simplificação na maioria dos casos, mas requer o idioma sem bloqueio para ter sucesso.

Experimente este código no seu navegador ou no node, e você verá que não há garantia de precisão, pelo contrário, o setTimeout chegará muito tarde:

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

A menos que o interpretador otimize o loop (o que não acontece no Chrome), você obterá algo na casa dos milhares. Remova o laço e você verá que é 500 no nariz ...

Davin
fonte
9

A única maneira de garantir que o código seja executado é colocar a lógica setTimeout em um processo diferente.

Use o módulo de processo filho para gerar um novo programa node.js que faz sua lógica e passa dados para esse processo por meio de algum tipo de fluxo (talvez tcp).

Desta forma, mesmo se algum código de bloqueio longo estiver sendo executado em seu processo principal, seu processo filho já foi iniciado e colocou um setTimeout em um novo processo e um novo thread e, portanto, será executado quando você espera.

Outras complicações estão em um nível de hardware, onde você tem mais threads em execução do que processos e, portanto, a troca de contexto causará atrasos (muito pequenos) em relação ao tempo esperado. Isso deve ser desprezível e, se for importante, você precisa considerar seriamente o que está tentando fazer, por que precisa de tal precisão e que tipo de hardware alternativo em tempo real está disponível para fazer o trabalho.

Em geral, usar processos filho e executar aplicativos de vários nós como processos separados junto com um balanceador de carga ou armazenamento de dados compartilhados (como redis) é importante para dimensionar seu código.

Raynos
fonte
9

setTimeouté uma espécie de Thread , ele mantém uma operação por um determinado tempo e executa.

setTimeout(function,time_in_mills);

aqui, o primeiro argumento deve ser um tipo de função; por exemplo, se você deseja imprimir seu nome após 3 segundos, seu código deve ser algo como abaixo.

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

O ponto chave a ser lembrado é: o que quer que você queira fazer usando o setTimeoutmétodo, faça-o dentro de uma função . Se você quiser chamar algum outro método analisando alguns parâmetros, seu código deve ser assim:

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

setTimeout(callback,t)é usado para executar o retorno de chamada após pelo menos t milissegundos . O atraso real depende de muitos fatores externos, como granularidade do cronômetro do sistema operacional e carga do sistema.

Portanto, existe a possibilidade de que seja chamado um pouco depois do tempo definido, mas nunca será chamado antes.

Um cronômetro não pode abranger mais de 24,8 dias.

Siva Prakash
fonte