¿Cómo puedo esperar en Node.js (JavaScript)? Necesito hacer una pausa por un período de tiempo

385

Estoy desarrollando un script de consola para necesidades personales. Necesito poder hacer una pausa durante un período prolongado de tiempo, pero, según mi investigación, Node.js no tiene forma de detenerse según sea necesario. Se está volviendo difícil leer la información de los usuarios después de un período de tiempo ... He visto algo de código, pero creo que tienen que tener otro código dentro de ellos para que funcionen, como:

    setTimeout(function() {
    }, 3000);

Sin embargo, necesito todo después de esta línea de código para ejecutar después del período de tiempo.

Por ejemplo,

    // start of code
    console.log('Welcome to my console,');

    some-wait-code-here-for-ten-seconds...

    console.log('Blah blah blah blah extra-blah');
    // end of code

También he visto cosas como

    yield sleep(2000);

Pero Node.js no reconoce esto.

¿Cómo puedo lograr esta pausa extendida?

Christopher Allen
fuente
3
¿qué tal si marcas a una de estas personas como respuesta correcta :)
Billybonks
1
@ Christopher Allen, tal vez no sea relevante, pero hace el trabajo: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Haitham Sweilem
El nodo-sueño módulo de NPM puede hacer el truco (sin embargo, sólo lo utilizaría para depurar)
Julian Soro

Respuestas:

211

La mejor manera de hacerlo es dividir su código en múltiples funciones, como esta:

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

Es similar a la solución de JohnnyHK , pero mucho más ordenada y fácil de extender.

Elliot Bonneville
fuente
77
@LucasSeveryn Estás haciendo algo mal, entonces. Este es un patrón de diseño central.
Elliot Bonneville
¿Y qué haces cuando el inicio de la función no está solo a una función de distancia, sino a 10 archivos del código que debe ser desincronizado?
Cyril Duchon-Doris
10
@ CyrilDuchon-Doris ¿Qué?
AturSams
3
use la respuesta de @ machineghost para una solución elegante basada en promesas que no requiere código personalizado y admite esperar / asíncrono
Dane Macaulay
Esto es asíncrono, significa que si la función1 termina antes de 3 segundos, comienzan a superponerse entre sí. Entonces ni siquiera puede regresar y casi nunca es utilizable. La única forma que encontré hasta ahora fue usardebugger;
Cristian Traìna
428

Una nueva respuesta a una vieja pregunta. Hoy ( enero 2017 junio 2019) es mucho más fácil. Puede usar la nueva async/awaitsintaxis . Por ejemplo:

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/awaitde forma inmediata sin instalar y complementos, debe usar node-v7 o node-v8, usando la --harmonybandera.

Actualización de junio de 2019: al usar las últimas versiones de NodeJS, puede usarlo de inmediato. No es necesario proporcionar argumentos de línea de comando. Incluso Google Chrome lo admite hoy.

Actualización de mayo de 2020: pronto podrá utilizar la awaitsintaxis fuera de una función asíncrona. En el nivel superior como en este ejemplo

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

La propuesta está en la etapa 3 . Puede usarlo hoy usando webpack 5 (alfa),

Más información:

Aminadav Glickshtein
fuente
55
Creo que olvidó la awaitpalabra clave delante desleep(1000)
Ryan Jarvis
66
Esta realmente debería ser la respuesta, el paquete sleep node.js puede ser problemático.
Stuart Allen
44
La mejor solución de hecho
Kostas Siabanis
40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));Para fanáticos de línea como yo :)
Predrag Stojadinović
21
let sleep = require('util').promisify(setTimeout);funciona en el Nodo 7.6+ y mejora la legibilidad
BrianHVB
217

La solución más corta sin ninguna dependencia:

await new Promise(resolve => setTimeout(resolve, 5000));
k06a
fuente
14
"El colmo de la sofisticación es la simplicidad". - Clare Booth Luce. Esta es, de lejos, la mejor respuesta, OMI.
arnold
3
Obviamente, esto es mucho más reciente que las otras respuestas, pero es el más elegante a partir de 2018 que hace el trabajo en 1 línea sin ningún otro impacto en el código.
Herick
1
Este es bueno. Sin embargo, debe usar NodeJS 7.6.0 o superior. No funcionará en versiones anteriores.
Ryan Shillington
55
let sleep = require('util').promisify(setTimeout);son tres caracteres más largos pero reutilizables y más legibles en la OMI
BrianHVB
2
Para evitar una queja formal, llámelo en resolvelugar de done. Es decirawait new Promise(resolve => setTimeout(resolve, 5000))
Darren Cook, el
150

Ponga el código que desea ejecutar después del retraso dentro de la setTimeoutdevolución de llamada:

console.log('Welcome to My Console,');
setTimeout(function() {
    console.log('Blah blah blah blah extra-blah');
}, 3000);
JohnnyHK
fuente
2
Esto es terriblemente desordenado y generalmente es una mala práctica, especialmente si el OP quiere que el resto del programa se ejecute después de ese retraso. Mira mi respuesta.
Elliot Bonneville
32
@ElliotBonneville Es solo un ejemplo para ilustrar el concepto. Obviamente, podría (debería) factorizar el código en una llamada de función en lugar de usar código en línea, como en cualquier otro lugar.
JohnnyHK
@ChristopherKemp: Resulta que Node.js tiene una solución para esto llamado node-fibers. Echale un vistazo.
Elliot Bonneville
Esta es una gran solución, especialmente si no desea ejecutar ningún código, solo desea imitar un método de "suspensión" dentro de un bucle. No hay nada malo con este ejemplo.
Halfstop
esto es perfecto para retrasar las peticiones procedentes de clientes, he utilizado este método para probar mis hiladores de carga en el lado del cliente
Moein rahimi
145

Esta es una técnica de bloqueo simple:

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

Se está bloqueando en la medida en que nada más sucederá en su script (como devoluciones de llamada). Pero como se trata de un script de consola, ¡tal vez sea lo que necesita!

atlex2
fuente
27
horrible o no, hace un simple wait, bloquea y funciona para algún propósito de prueba. Exactamente lo que estaba buscando.
Clijsters
8
responde perfectamente a la pregunta sin libs de terceros, y es simple. Sin embargo, la gente dice "horrible" ... Esto es genial, por ejemplo, para simular una gran carga de CPU, etc. Por cierto, muy similar a este phpied.com/sleep-in-javascript
Don Cheadle
2
Además, este es el enfoque requerido si está intentando simular algo que ralentizaría el ciclo de eventos, lo que también ralentizaría a otros usuarios / conexiones. Agregar devoluciones de llamada demoradas no afectará a otros usuarios / conexiones.
Don Cheadle
13
Se trata de una spin-wait.
Mike Atlas
77
@ Ali, ese parece ser el objetivo.
Félix Gagnon-Grenier
63

En el nodo 7.6.0 o superior

El nodo admite esperar de forma nativa:

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

entonces si puedes usar funciones asíncronas:

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

o:

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

En versiones anteriores de Node (respuesta original)

Quería una suspensión asincrónica que funcionara en Windows y Linux, sin acaparar mi CPU con un ciclo de tiempo largo. Probé el paquete de suspensión pero no se instaló en mi caja de Windows. Terminé usando:

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

Para instalarlo, escriba:

npm install system-sleep

En su código

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

Funciona de maravilla.

Ryan Shillington
fuente
1
gran respuesta gracias - hizo posible un código imposible - esto NO es un spin-wait
danday74
1
En investigaciones posteriores, este módulo se basa en deasync, que se bifurca de otro repositorio de deasync github. El repositorio original advierte que no lo use, ya que es un truco. Funciona pero no en todas las plataformas, por lo que si necesita una solución multiplataforma, evite esta.
danday74
1
Sí, nunca he experimentado problemas con él y es genial para desarrolladores: creo que depende de C o C ++, que está ampliamente disponible, pero supongo que en algunas máquinas no lo es y eso es cuando falla, por eso causa problemas - si falla, el sistema de suspensión cae a una espera de giro que congelará la ejecución del código
danday74
1
Este paquete no funciona en Mac OS y, por lo tanto, no es compatible entre sí y, por lo tanto, no funciona. Ver github.com/jochemstoel/nodejs-system-sleep/issues/4
nuzzolilo
1
@ Nuzzolilo Eso es raro. Tenemos un desarrollador en Mac OS y nunca mencionó nada malo. Sospecho que es una versión específica de Mac OS. También actualicé esta respuesta ya que el sueño está básicamente integrado en las nuevas versiones de NodeJS.
Ryan Shillington
62

Función de sueño simple y elegante usando Javascript moderno

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

Sin dependencias, sin devolución de llamada infierno; Eso es :-)


Considerando el ejemplo dado en la pregunta, así es como dormiríamos entre dos registros de consola:

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

main();

El "inconveniente" es que su función principal ahora también debe serlo async. Pero, teniendo en cuenta que ya está escribiendo un código Javascript moderno, probablemente esté (¡o al menos debería hacerlo!) Utilizando async/ awaittodo su código, por lo que esto realmente no es un problema. Todos los navegadores modernos de hoy lo admiten .

Dando una pequeña idea de la sleepfunción para aquellos que no están acostumbrados async/awaity operadores de flecha gruesa, esta es la forma detallada de escribirlo:

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

Sin embargo, el uso del operador de flecha gruesa lo hace aún más pequeño (y más elegante).

Lucio Paiva
fuente
1
También puede escribir la función dormir sin asyncy dejando todo lo demás igual. Probablemente es más claro con el asyncsin embargo.
Kevin Peña
Buena captura, @ KevinPeña. De hecho, creo que lo prefiero sin él async. Edité mi respuesta con su sugerencia. No creo que aclare el código; para eso, recurriría a JSDoc en su lugar.
Lucio Paiva
8
Me gusta tener la siguiente const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
frase
46

Puede usar este www.npmjs.com/package/sleep

var sleep = require('sleep');
sleep.sleep(10); // sleep for ten seconds
Aleksej
fuente
44
Funciona bien MacOS, pero encuentra errores CentOSdebido a errores de node_gyp . Parece que no es portátil.
AechoLiu
1
quizás un problema causado no por el sistema operativo, sino por la construcción del nodo
Aleksej
34

Esta pregunta es bastante antigua, pero recientemente V8 ha agregado generadores que pueden lograr lo que solicitó el OP. Los generadores son generalmente más fáciles de usar para las interacciones asíncronas con la ayuda de una biblioteca como suspender o gen-run .

Aquí hay un ejemplo usando suspender:

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

Lectura relacionada (a modo de autopromoción descarada): ¿Cuál es el gran problema con los generadores? .

jmar777
fuente
2
Buena respuesta - Pero debería leeryield setTimeout(suspend.resume(), 10000);
edhubbell
1
Gracias, @edhubbell. Esta respuesta se basó en una versión muy antigua de suspender, pero tienes razón con respecto a la última. Actualizaré la respuesta.
jmar777
¿Para qué versión de nodo es esto?
danday74
1
@ danday74 No recuerdo exactamente cuándo estaban sin marcar, pero han estado presentes desde v0.12 detrás de la --harmonybandera, y de acuerdo con node.green, al menos están disponibles sin ninguna bandera desde v4.8.4: node.green/#ES2015-functions-generators-basic-functionality . Sin embargo, tenga en cuenta que la nueva async/awaitsintaxis proporciona una mejor solución a esto ahora, sin necesidad de bibliotecas adicionales como suspend. Consulte esta respuesta para ver un ejemplo: stackoverflow.com/a/41957152/376789 . Las funciones asíncronas están disponibles (sin banderas) desde la v7.10.
jmar777
20

En Linux / nodejs esto funciona para mí:

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

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

Está bloqueando, pero no es un ciclo de espera ocupado.

El tiempo que especifique es en segundos, pero puede ser una fracción. No sé si otros sistemas operativos tienen un comando similar.

Bart Verheijen
fuente
sleepes bastante omnipresente y útil desde los primeros días de UNIX en.wikipedia.org/wiki/Sleep_%28Unix%29 . Sólo "no es raro" OS que tal vez no existiría es Windows (sin embargo no se podría intentarchild_process.spawnSync('timeout', ['/T', '10'])
humanityANDpeace
17

Si desea "codificar golf", puede hacer una versión más corta de algunas de las otras respuestas aquí:

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

Pero realmente la respuesta ideal en mi opinión es usar la utilbiblioteca de Node y su promisifyfunción, que está diseñada exactamente para este tipo de cosas (hacer versiones basadas en promesas de cosas previamente no basadas en promesas):

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

En cualquier caso, puede pausar simplemente usando awaitpara llamar a su sleepfunción:

await sleep(1000); // sleep for 1s/1000ms
maquina
fuente
En realidad, usando util.promisify, no es posible llamar a dormir sin proporcionar también una devolución de llamada. nodejs.org/api/…
Howie
1
Te estás perdiendo el await. Crea una devolución de llamada, pausa las cosas y luego reinicia el código cuando vuelve la devolución de llamada. La devolución de llamada regresa con un valor, pero no nos importa en este caso, por lo que no hay nada a la izquierda await.
machineghost
1
escríbalo en una línea para obtener el código golf;) const sleep = require ('util'). promisify (setTimeout);
Fabian
14

Recientemente he creado una abstracción más simple llamada wait.for para llamar a funciones asíncronas en modo de sincronización (basado en fibras de nodo). También hay una versión basada en los próximos generadores ES6.

https://github.com/luciotato/waitfor

Usando wait.for , puede llamar a cualquier función asíncrona de nodejs estándar, como si fuera una función de sincronización, sin bloquear el bucle de eventos del nodo.

Puede codificar secuencialmente cuando lo necesite, que es, (supongo) perfecto para simplificar sus scripts para uso personal.

usando wait.for su 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. 

También se puede llamar a cualquier función asíncrona en modo Sincronización . Mira los ejemplos.

Lucio M. Tato
fuente
TypeError: Object #<Object> has no method 'miliseconds'
CaffeineAddiction
el comentario dice: "// definido en waitfor / paralell-tests.js" tómalo de ese archivo.
Lucio M. Tato
2
Después de obtenerlo, wait.for/paralell-tests.jsencontré otros errores relacionados con propiedades indefinidas, etc. Así que también tuve que copiarlos. ¿Por qué no organizas el código de manera que no sea necesario?
user907860
¡Wait.for y otras soluciones de fibra me han abierto un mundo completamente nuevo! Votaría esto un millón de veces si pudiera. Aunque la mayoría de la comunidad de nodejs se opone a las fibras, creo que son una adición fantástica y definitivamente tienen su lugar cuando se trata del infierno de devolución de llamadas.
Levi Roberts
7

Dado que el motor de JavaScript (v8) ejecuta código basado en la secuencia de eventos en la cola de eventos, no es estricto que JavaScript active exactamente la ejecución después de un tiempo especificado. Es decir, cuando configura unos segundos para ejecutar el código más tarde, el código de activación se basa puramente en la secuencia en la cola de eventos. Por lo tanto, activar la ejecución de código puede llevar más tiempo que el especificado.

Entonces Node.js sigue,

process.nextTick()

para ejecutar el código más tarde en su lugar setTimeout (). Por ejemplo,

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

Con ES6 compatible con Promises, podemos usarlos sin ayuda de terceros.

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.

Entonces úsalo así:

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

Tenga en cuenta que la doWhatfunción se convierte en la resolvedevolución de llamada dentro delnew Promise(...) .

También tenga en cuenta que este es el sueño ASINCRÓNICO. No bloquea el bucle de eventos. Si necesita bloquear el sueño, use esta biblioteca que realiza el bloqueo del sueño con la ayuda de enlaces C ++. (Aunque la necesidad de un bloqueo de suspensión en Nodo como entornos asíncronos es rara).

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

codificador de árboles
fuente
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 anterior es el efecto secundario de resolver el problema del infierno de devolución de llamada asincrónica de Javascript. Esta es también la razón por la que creo que hace que Javascript sea un lenguaje útil en el backend. En realidad, esta es la mejora más emocionante introducida al Javascript moderno en mi opinión. Para comprender completamente cómo funciona, es necesario comprender completamente cómo funciona el generador. La functionpalabra clave seguida de a *se llama función generadora en Javascript moderno. El paquete npm coproporcionó una función de corredor para ejecutar un generador.

Esencialmente, la función de generador proporcionó una forma de pausar la ejecución de una función con una yieldpalabra clave, al mismo tiempo, yielden una función de generador hizo posible intercambiar información entre el generador y la persona que llama. Esto proporcionó un mecanismo para que la persona que llama extraiga los datos de una promisellamada asincrónica y devuelva los datos resueltos al generador. Efectivamente, realiza una llamada asincrónica sincrónica.

Qian Chen
fuente
Si bien este código puede responder la pregunta, proporcionar un contexto adicional sobre cómo y / o por qué resuelve el problema mejoraría el valor a largo plazo de la respuesta.
Donald Duck
Gracias @DonaldDuck. El código en mi respuesta es probablemente la parte más emocionante de la mejora en Javascript. Estoy tan sorprendido de que algunas personas súper inteligentes piensen de esta manera para resolver el problema del infierno de devolución de llamada.
Qian Chen
1

Este es un módulo con sabor moment.js basado en el enfoque de bloqueo sucio sugerido por @ atlex2 . Use esto solo para pruebas .

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 - Restablece a Monica
fuente
0

Si solo necesita suspender para probar su ejecución actual de subprocesos, intente esto:

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
fuente
0

simple, vamos a esperar 5 segundos para que suceda algún evento (eso se indicaría mediante la variable hecha establecida en verdadero en otro lugar del código) o cuando caduque el tiempo de espera que verificaremos 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
fuente
0

En lugar de hacer todos estos setTimeout, dormir y muchas otras cosas, es mejor usar

const delay = require('delay');
await delay(2000); // will wait for 2 seconds before executing next line of code
Rishab
fuente
¿Puedes explicar qué es "retraso" y vincularlo a sus documentos? ¿Por qué es mejor?
Boycy
-2

Las otras respuestas son geniales, pero pensé que tomaría un tacto diferente.

Si todo lo que realmente está buscando es reducir la velocidad de un archivo específico en Linux:

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

nodo myprog archivo lento

Esto dormirá 1 segundo cada cinco líneas. El programa de nodo irá tan lento como el escritor. Si está haciendo otras cosas, continuarán a la velocidad normal.

El mkfifo crea una tubería primero en entrar, primero en salir. Es lo que hace que esto funcione. La línea perl escribirá tan rápido como quieras. El $ | = 1 dice no amortiguar la salida.

Pete
fuente
-3

Reuní, después de haber leído las respuestas en esta pregunta, una función simple que también puede hacer una devolución de llamada, si lo necesita:

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

Para más información sobre

yield sleep(2000); 

deberías revisar Redux-Saga . Pero es específico para su elección de Redux como su marco modelo (aunque estrictamente no es necesario).

Arseni Buinitski
fuente