Como posso esperar no Node.js (Javascript), preciso fazer uma pausa por um período de tempo

385

Estou desenvolvendo um console como script para necessidades pessoais. Eu preciso fazer uma pausa por um longo período de tempo, mas, a partir da minha pesquisa, o node.js não tem como parar conforme necessário. Está ficando difícil ler as informações dos usuários após um período de tempo ... Eu vi alguns códigos por aí, mas acredito que eles precisam ter outro código dentro deles para que funcionem, como:

setTimeout(function() {
}, 3000);

No entanto, eu preciso de tudo após esta linha de código para executar após o período de tempo.

Por exemplo,

//start-of-code
console.log('Welcome to My Console,');
some-wait-code-here-for-ten-seconds..........
console.log('Blah blah blah blah extra-blah');
//endcode. 

Eu também vi coisas como

yield sleep(2000);

Mas o node.js não reconhece isso.

Como posso conseguir essa pausa prolongada?

Christopher Allen
fonte
3
hows sobre você marcar uma dessas pessoas como resposta correta :)
Billybonks
11
@ Christopher Allen, Talvez não seja relevante, mas faz o trabalho: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Haitham Sweilem 4/16
O módulo node-sleep npm pode fazer o truque (no entanto, eu só o usaria para depuração) #
Julian Soro

Respostas:

211

A melhor maneira de fazer isso é dividir seu código em várias funções, assim:

function function1() {
    // stuff you want to happen right away
    console.log('Welcome to My Console,');
}

function function2() {
    // all the stuff you want to happen after that pause
    console.log('Blah blah blah blah extra-blah');
}

// call the first chunk of code right away
function1();

// call the rest of the code and have it execute after 3 seconds
setTimeout(function2, 3000);

É semelhante à solução do JohnnyHK , mas muito mais simples e fácil de estender.

Elliot Bonneville
fonte
7
@LucasSeveryn Você está fazendo algo errado, então. Este é um padrão de design básico.
Elliot Bonneville
E o que você faz quando o início da função não está apenas a uma função, mas a 10 arquivos do código que precisa ser dessincronizado?
Cyril Duchon-Doris
10
@ CyrilDuchon-Doris O que?
AturSams
3
usar @ resposta de machineghost para uma solução baseada elegante promessa que não requer nenhum código personalizado e suportes aguardam / async
Dane Macaulay
Isso é assíncrono, significa que, se a função1 terminar antes de 3 segundos, eles começarão a se sobrepor. Então você nem pode voltar e quase nunca é utilizável. A única maneira que eu encontrei até agora estava usando #debugger;
Cristian Traìna
427

Uma nova resposta para uma pergunta antiga. Hoje ( janeiro de 2017 junho de 2019) é muito mais fácil. Você pode usar a nova async/awaitsintaxe . Por exemplo:

async function init() {
  console.log(1);
  await sleep(1000);
  console.log(2);
}

function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}   

Para usar async/awaitimediatamente, sem instalar e plug-ins, é necessário usar o nó-v7 ou o nó-v8, usando o --harmonysinalizador

Atualização junho de 2019: Ao usar as versões mais recentes do NodeJS, você pode usá-lo imediatamente. Não há necessidade de fornecer argumentos de linha de comando. Até o Google Chrome o suporta hoje.

Atualização em maio de 2020: em breve você poderá usar a awaitsintaxe fora de uma função assíncrona. No nível superior, como neste exemplo

await sleep(1000)
function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
} 

A proposta está no estágio 3 . Você pode usá-lo hoje usando o webpack 5 (alpha),

Mais informações:

Aminadav Glickshtein
fonte
5
Eu acho que você esqueceu a awaitpalavra - chave na frente desleep(1000)
Ryan Jarvis
6
Essa realmente deve ser a resposta, o pacote sleep node.js pode ser problemático.
Stuart Allen
4
Melhor solução, na verdade
Kostas Siabanis
40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));Para os fãs de oneliner como eu :)
Predrag Stojadinovic
21
let sleep = require('util').promisify(setTimeout);funciona no nó 7.6+ e melhora a legibilidade
BrianHVB
216

A solução mais curta sem nenhuma dependência:

await new Promise(resolve => setTimeout(resolve, 5000));
k06a
fonte
14
"O auge da sofisticação é a simplicidade." - Clare Booth Luce. Esta é de longe a melhor resposta, IMO.
precisa
3
Obviamente, isso é muito mais recente do que as outras respostas, mas é o mais elegante a partir de 2018 que realiza o trabalho em uma linha sem nenhum outro impacto no código.
Herick
11
Essa é boa. Você deve estar usando o NodeJS 7.6.0 ou superior. Não funcionará em versões mais antigas.
Ryan Shillington
5
let sleep = require('util').promisify(setTimeout);é três personagens mais tempo, mas reutilizável e mais legível imo
BrianHVB
2
Para evitar uma reclamação de exclusão, chame-a em resolvevez de done. Ou sejaawait new Promise(resolve => setTimeout(resolve, 5000))
Darren Cozinhe
150

Coloque o código que você deseja executar após o atraso no setTimeoutretorno de chamada:

console.log('Welcome to My Console,');
setTimeout(function() {
    console.log('Blah blah blah blah extra-blah');
}, 3000);
JohnnyHK
fonte
2
Essa é uma prática extremamente confusa e geralmente ruim, especialmente se o OP desejar que o restante do programa seja executado após esse atraso. Veja minha resposta.
precisa saber é o seguinte
32
@ ElliotBonneville É apenas um exemplo para ilustrar o conceito. Obviamente, você pode (deve) incluir o código em uma chamada de função em vez de usar o código embutido, como em qualquer outro lugar.
precisa saber é o seguinte
@ChristopherKemp: Acontece que o Node.js tem uma solução para isso chamada node-fibers. Confira.
precisa saber é o seguinte
Essa é uma ótima solução, principalmente se você não deseja executar nenhum código, apenas imitando um método "sleep" dentro de um loop. Nada de errado com este exemplo.
Halfstop 25/10/16
este é perfeito para atrasar sobre os pedidos provenientes de cliente, eu usei essa abordagem para testar meus carregamento spinners no lado do cliente
Moein Rahimi
145

Esta é uma técnica simples de bloqueio:

var waitTill = new Date(new Date().getTime() + seconds * 1000);
while(waitTill > new Date()){}

Está bloqueando na medida em que nada mais acontecerá no seu script (como retornos de chamada). Mas como esse é um script de console, talvez seja o que você precisa!

atlex2
fonte
27
horrível ou não, faz um simples wait, bloqueia e funciona para algum propósito de teste. Exatamente o que eu estava procurando.
Clijsters
8
responde perfeitamente à pergunta sem bibliotecas de terceiros e é simples. No entanto, as pessoas dizem "horrível" .... Isso é ótimo por exemplo, para simular a carga da CPU pesado etc. Btw muito semelhante a esta phpied.com/sleep-in-javascript
Don Cheadle
2
Além disso, essa é a abordagem necessária se você estiver tentando simular algo que diminuiria o ciclo do evento, retardando também outros usuários / conexões. Adicionar retornos de chamada atrasados ​​não afetará outros usuários / conexões.
Don Cheadle
13
Isto é um spin-wait.
Mike Atlas
7
@ Ali que parece ser o objetivo.
Félix Gagnon-Grenier
63

No nó 7.6.0 ou superior

O nó suporta a espera nativamente:

const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));

se você pode usar funções assíncronas:

await sleep(10000); // sleep for 10 seconds

ou:

sleep(10000).then(() => {
  // This will execute 10 seconds from now
});

Nas versões mais antigas do nó (resposta original)

Eu queria um sono assíncrono que funcionasse no Windows e Linux, sem sobrecarregar minha CPU com um longo loop. Tentei o pacote de suspensão, mas ele não foi instalado na minha caixa do Windows. Acabei usando:

https://www.npmjs.com/package/system-sleep

Para instalá-lo, digite:

npm install system-sleep

No seu código,

var sleep = require('system-sleep');
sleep(10*1000); // sleep for 10 seconds

Funciona como um encanto.

Ryan Shillington
fonte
11
Grande resposta graças - ele fez algum código impossível possível - este não é um spin-espera
danday74
11
Em pesquisas adicionais, este módulo conta com o deasync, que é extraído de outro repositório do deasync github. O repo original adverte para não usá-lo, pois é um hack. Funciona, mas não em todas as plataformas, portanto, se você precisar de uma solução de plataforma cruzada, evite esta.
danday74
11
Sim, eu nunca experimentei probs com ele e é ótimo para dev - sob o capô, acredito que ele depende de C ou C ++, que está amplamente disponível, mas acho que em algumas máquinas não é e é quando falha, e é por isso que causa problemas - se ele falhar sono sistema cai de volta para uma espera de rotação que vai congelar a execução de código
danday74
11
Este pacote não funciona no Mac OS e, portanto, não é compatível com outras versões e, portanto, não é viável. Veja github.com/jochemstoel/nodejs-system-sleep/issues/4
nuzzolilo
11
@Nuzzolilo Isso é estranho. Temos um desenvolvedor no Mac OS e ele nunca mencionou nada de errado. Eu suspeito que é uma versão específica do Mac OS. Também atualizei esta resposta, pois o sono é basicamente incorporado às versões mais recentes do NodeJS.
Ryan Shillington
62

Função de sono simples e elegante usando Javascript moderno

function sleep(millis) {
    return new Promise(resolve => setTimeout(resolve, millis));
}

Sem dependências, sem retorno de chamada; é isso aí :-)


Considerando o exemplo dado na pergunta, é assim que dormimos entre dois logs do console:

async function main() {
    console.log("Foo");
    await sleep(2000);
    console.log("Bar");
}

main();

A "desvantagem" é que sua função principal agora também precisa ser async. Mas, considerando que você já está escrevendo um código Javascript moderno, provavelmente você está (ou pelo menos deveria estar!) Usando async/ em awaittodo o seu código, então isso realmente não é um problema. Todos os navegadores modernos hoje suportam .

Fornecendo uma pequena visão da sleepfunção para aqueles que não estão acostumados async/awaite operadores de seta gorda, esta é a maneira detalhada de escrevê-la:

function sleep(millis) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () { resolve(); }, millis);
    });
}

No entanto, o uso do operador de seta gorda o torna ainda menor (e mais elegante).

Lucio Paiva
fonte
11
Você também pode escrever a função de suspensão sem a asynctecla e deixando tudo o mesmo. Provavelmente é mais claro com o asyncpensamento.
Kevin Peña
Boa captura, @ KevinPeña. De fato, acho que prefiro sem async. Editou minha resposta com sua sugestão. Eu não acho que isso torna o código mais claro; por isso, eu recorreria ao JSDoc.
Lucio Paiva
8
Eu gosto de ter o seguinte one-liner em meus scripts:const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
korya 23/01
46

Você pode usar este site www.npmjs.com/package/sleep

var sleep = require('sleep');
sleep.sleep(10); // sleep for ten seconds
Aleksej
fonte
4
Ele funciona bem MacOS, mas encontra erros CentOSdevido a erros do node_gyp . Parece não ser portátil.
AechoLiu
11
talvez problema não causada por OS, mas pela construção nó
Aleksej
34

Essa pergunta é bastante antiga, mas recentemente a V8 adicionou geradores que podem realizar o que o OP solicitou. Geralmente, os geradores são mais fáceis de usar para interações assíncronas com a assistência de uma biblioteca, como suspender ou executar com gen .

Aqui está um exemplo usando o suspend:

suspend(function* () {
    console.log('Welcome to My Console,');
    yield setTimeout(suspend.resume(), 10000); // 10 seconds pass..
    console.log('Blah blah blah blah extra-blah');
})();

Leitura relacionada (por meio de autopromoção desavergonhada): Qual é o grande problema dos geradores? .

jmar777
fonte
2
Boa resposta - Mas deve leryield setTimeout(suspend.resume(), 10000);
edhubbell
11
Obrigado, @edhubbell. Esta resposta foi baseada em uma versão muito antiga do suspend, mas você está certo quanto à mais recente. Vou atualizar a resposta.
jmar777
qual é a versão do nó?
precisa saber é o seguinte
11
@ danday74 Não me lembro exatamente quando eles não foram sinalizados, mas existem desde a v0.12 atrás da --harmonybandeira e, de acordo com node.green, estão pelo menos disponíveis sem sinalizadores desde a v4.8.4: node.green/#ES2015-functions-generators-basic-functionality . Observe, no entanto, que a async/awaitsintaxe mais recente fornece uma solução melhor para isso agora, sem a necessidade de bibliotecas extras como suspend. Veja esta resposta para um exemplo: stackoverflow.com/a/41957152/376789 . As funções assíncronas estão disponíveis (sem sinalizadores) desde a v7.10.
precisa saber é o seguinte
20

No Linux / nodejs, isso funciona para mim:

const spawnSync = require ('child_process'). spawnSync;

var sono = spawnSync ('sono', [1.5]);

Está bloqueando, mas não é um ciclo de espera ocupado.

O tempo especificado é em segundos, mas pode ser uma fração. Não sei se outros sistemas operacionais têm um comando semelhante.

Bart Verheijen
fonte
sleepé praticamente onipresente e muito útil desde os primeiros dias do UNIX en.wikipedia.org/wiki/Sleep_%28Unix%29 . Apenas "não raro" os que talvez não existiria é windows (no entanto, há que você poderia tentarchild_process.spawnSync('timeout', ['/T', '10'])
humanityANDpeace
17

Se você deseja "codificar golfe", você pode fazer uma versão mais curta de algumas das outras respostas aqui:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

Mas, na verdade, a resposta ideal, na minha opinião, é usar a utilbiblioteca do Node e sua promisifyfunção, projetada exatamente para esse tipo de coisa (criando versões baseadas em promessas de coisas não baseadas em promessas anteriormente existentes):

const util = require('util');
const sleep = util.promisify(setTimeout);

Em ambos os casos, você pode pausar simplesmente usando awaitpara chamar sua sleepfunção:

await sleep(1000); // sleep for 1s/1000ms
machineghost
fonte
Na verdade, usando util.promisify, não é possível chamar suspensão sem fornecer também um retorno de chamada. nodejs.org/api/…
Howie
11
Você está sentindo falta do await. Ele meio que cria um retorno de chamada, pausa as coisas e reinicia o código quando esse retorno de chamada. O retorno de chamada retorna com um valor, mas não nos preocupamos com isso neste caso, portanto, não resta nada à esquerda await.
precisa
11
escreva-o em uma linha para usar o código golf;) const sleep = require ('util'). promisify (setTimeout);
Fabian
14

Recentemente, criei uma abstração mais simples chamada wait.for para chamar funções assíncronas no modo de sincronização (com base nas fibras do nó). Há também uma versão baseada nos próximos Geradores ES6.

https://github.com/luciotato/waitfor

Usando wait.for , você pode chamar qualquer função nodejs assíncrona padrão, como se fosse uma função de sincronização, sem bloquear o loop de eventos do nó.

Você pode codificar sequencialmente quando precisar, o que é (suponho) perfeito para simplificar seus scripts para uso pessoal.

usando wait.for, seu código será:

require('waitfor')

..in a fiber..
//start-of-code
console.log('Welcome to My Console,');
wait.miliseconds(10*1000); //defined in waitfor/paralell-tests.js - DOES NOT BLOCK
console.log('Blah blah blah blah extra-blah');
//endcode. 

Também qualquer função assíncrona pode ser chamada no modo Sincronizar . Veja os exemplos.

Lucio M. Tato
fonte
TypeError: Object #<Object> has no method 'miliseconds'
CaffeineAddiction
o comentário diz: "// definido em waitfor / paralell-tests.js" pegue-o nesse arquivo.
Lucio M. Tato 14/03
2
depois que o obtive, wait.for/paralell-tests.jsencontrei outros erros relacionados a propriedades indefinidas, etc. Então, eu também precisei copiá-los. Por que você não organiza o código de maneira que isso não seja necessário?
user907860
O Wait.for e outras soluções de fibra abriram um mundo totalmente novo para mim! Eu votaria nisso um milhão de vezes, se pudesse. Embora a maioria da comunidade nodejs se oponha às fibras, acho que elas são uma adição fantástica e definitivamente têm seu lugar quando se trata de um inferno de retorno de chamada.
Levi Roberts
7

Como o mecanismo javascript (v8) executa o código com base na sequência de eventos na fila de eventos, não é rigoroso que o javascript acione exatamente a execução após o tempo especificado. Ou seja, quando você define alguns segundos para executar o código posteriormente, o código de acionamento é puramente baseado na sequência na fila de eventos. Portanto, disparar a execução do código pode levar mais do que o tempo especificado.

Então o Node.js segue,

process.nextTick()

para executar o código posteriormente, em vez de setTimeout (). Por exemplo,

process.nextTick(function(){
    console.log("This will be printed later");
});
HILARUDEEN S ALLAUDEEN
fonte
2

Com o ES6 suportando Promises, podemos usá-los sem nenhum auxílio de terceiros.

const sleep = (seconds) => {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, (seconds * 1000));
    });
};

// We are not using `reject` anywhere, but it is good to
// stick to standard signature.

Em seguida, use-o assim:

const waitThenDo(howLong, doWhat) => {
    return sleep(howLong).then(doWhat);
};

Observe que a doWhatfunção se torna o resolveretorno de chamada dentro do new Promise(...).

Observe também que isso é sono ASSÍNCRONO. Não bloqueia o loop de eventos. Se você precisar bloquear o sono, use esta biblioteca que realiza o bloqueio do sono com a ajuda de ligações do C ++. (Embora a necessidade de um sono bloqueador no Node como ambientes assíncronos seja rara.)

https://github.com/erikdubbelboer/node-sleep

codificador de árvore
fonte
1
let co = require('co');
const sleep = ms => new Promise(res => setTimeout(res, ms));

co(function*() {
    console.log('Welcome to My Console,');
    yield sleep(3000);
    console.log('Blah blah blah blah extra-blah');
});

Este código acima é o efeito colateral do problema do inferno de retorno de chamada assíncrona do Javascript. Esta é também a razão pela qual acho que faz do Javascript uma linguagem útil no back-end. Na verdade, esta é a melhoria mais emocionante introduzida no Javascript moderno na minha opinião. Para entender completamente como ele funciona, como o gerador funciona precisa ser totalmente compreendido. A functionpalavra-chave seguida por a *é chamada de função geradora em Javascript moderno. O pacote npm coforneceu uma função runner para executar um gerador.

Essencialmente, a função gerador forneceu uma maneira de pausar a execução de uma função com a yieldpalavra - chave, ao mesmo tempo, yieldem uma função geradora, permitindo a troca de informações entre o gerador e o chamador. Isso forneceu um mecanismo para o chamador extrair dados de uma promisechamada assíncrona e passar os dados resolvidos de volta ao gerador. Efetivamente, ele faz uma chamada assíncrona síncrona.

Qian Chen
fonte
Embora esse código possa responder à pergunta, fornecer um contexto adicional sobre como e / ou por que resolve o problema melhoraria o valor a longo prazo da resposta.
Donald Duck
Obrigado @DonaldDuck. O código na minha resposta é provavelmente a parte mais interessante da melhoria do Javascript. Estou tão surpreso que algumas pessoas super inteligentes pensem dessa maneira para resolver o problema do inferno de retorno de chamada.
Qian Chen
1

Este é um módulo com formato moment.js, com base na abordagem de bloqueio sujo sugerida por @ atlex2 . Use isso apenas para testes .

const moment = require('moment');

let sleep = (secondsToSleep = 1) => {
    let sleepUntill = moment().add(secondsToSleep, 'seconds');
    while(moment().isBefore(sleepUntill)) { /* block the process */ }
}

module.exports = sleep;
Boaz - Restabelecer Monica
fonte
0

Se você apenas precisar suspender para fins de teste, a execução atual do encadeamento tente o seguinte:

function longExecFunc(callback, count) {

    for (var j = 0; j < count; j++) {
        for (var i = 1; i < (1 << 30); i++) {
            var q = Math.sqrt(1 << 30);
        }
    }
    callback();
}
longExecFunc(() => { console.log('done!')}, 5); //5, 6 ... whatever. Higher -- longer
Ivan Talalaev
fonte
0

simples, vamos esperar por 5 segundos para que algum evento aconteça (que seria indicado pela variável concluída configurada como true em outro lugar no código) ou quando o tempo limite expirar, verificaremos a cada 100 ms

    var timeout=5000; //will wait for 5 seconds or untildone
    var scope = this; //bind this to scope variable
    (function() {
        if (timeout<=0 || scope.done) //timeout expired or done
        {
            scope.callback();//some function to call after we are done
        }
        else
        {
            setTimeout(arguments.callee,100) //call itself again until done
            timeout -= 100;
        }
    })();
Stan Sokolov
fonte
0

Para algumas pessoas, a resposta aceita não está funcionando, encontrei esta outra resposta e está funcionando para mim: Como posso passar um parâmetro para um retorno de chamada setTimeout ()?

var hello = "Hello World";
setTimeout(alert, 1000, hello); 

'olá' é o parâmetro que está sendo passado, você pode passar todos os parâmetros após o tempo limite. Obrigado a @Fabio Phms pela resposta.

Dazt
fonte
0

Em vez de fazer todos esses setTimeout, sleep e muitas outras coisas, é melhor usar

const delay = require('delay');
await delay(2000); // will wait for 2 seconds before executing next line of code
Rishab
fonte
Você pode explicar o que é 'atraso' e vincular-se aos documentos? Porque é melhor?
boycy
-2

As outras respostas são ótimas, mas pensei em usar um tato diferente.

Se tudo o que você está realmente procurando é diminuir a velocidade de um arquivo específico no linux:

 rm slowfile; mkfifo slowfile; perl -e 'select STDOUT; $| = 1; while(<>) {print $_; sleep(1) if (($ii++ % 5) == 0); }' myfile > slowfile  &

nó myprog slowfile

Isso durará 1 segundo a cada cinco linhas. O programa do nó será tão lento quanto o gravador. Se estiver fazendo outras coisas, eles continuarão na velocidade normal.

O mkfifo cria um tubo primeiro a entrar, primeiro a sair. É o que faz isso funcionar. A linha perl escreverá tão rápido quanto você desejar. O $ | = 1 diz não armazenar em buffer a saída.

Pete
fonte
-3

Eu montei, depois de ler as respostas nesta pergunta, uma função simples que também pode fazer um retorno de chamada, se você precisar:

function waitFor(ms, cb) {
  var waitTill = new Date(new Date().getTime() + ms);
  while(waitTill > new Date()){};
  if (cb) {
    cb()
  } else {
   return true
  }
}
Netsi1964
fonte
-4

Para mais informações sobre

yield sleep(2000); 

você deve verificar o Redux-Saga . Mas é específico para sua escolha do Redux como sua estrutura de modelo (embora estritamente não seja necessário).

Arseni Buinitski
fonte