Come posso aspettare in Node.js (Javascript), devo fare una pausa per un certo periodo di tempo


385

Sto sviluppando una console come script per esigenze personali. Devo essere in grado di mettere in pausa per un lungo periodo di tempo, ma, dalla mia ricerca, node.js non ha modo di fermarsi come richiesto. Sta diventando difficile leggere le informazioni degli utenti dopo un certo periodo di tempo ... Ho visto del codice là fuori, ma credo che debbano avere un altro codice al loro interno per poter funzionare come:

setTimeout(function() {
}, 3000);

Tuttavia, ho bisogno di tutto dopo questa riga di codice per eseguire dopo il periodo di tempo.

Per esempio,

//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. 

Ho visto anche cose del genere

yield sleep(2000);

Ma node.js non lo riconosce.

Come posso ottenere questa pausa prolungata?


3
Salve su di te contrassegnare una di queste persone come risposta corretta :)
Billybonks

1
@Christopher Allen, forse non pertinente, ma fa il lavoro: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Haitham Sweilem,

Il modulo npm node-sleep potrebbe fare il trucco (tuttavia, lo userei solo per il debug)
Julian Soro,

Risposte:


211

Il modo migliore per farlo è suddividere il codice in più funzioni, in questo modo:

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

È simile alla soluzione di JohnnyHK , ma molto più ordinata e più facile da estendere.


7
@LucasSeveryn Stai facendo qualcosa di sbagliato, allora. Questo è un modello di progettazione di base.
Elliot Bonneville,

E cosa fai quando l'avvio della funzione non è solo a una funzione, ma a 10 file di distanza dal codice che deve essere asincronizzato?
Cyril Duchon-Doris,

10
@ CyrilDuchon-Doris Cosa?
AturSams,

3
usa la risposta di @ machineghost per un'elegante soluzione basata sulla promessa che non richiede codice personalizzato e supporta wait / async
Dane Macaulay,

Questo è asincrono, significa che se la funzione1 termina prima di 3 secondi, iniziano a sovrapporsi. Quindi non puoi nemmeno tornare ed è quasi mai utilizzabile. L'unico modo che ho trovato finora era usaredebugger;
Cristian Traìna il

428

Una nuova risposta a una vecchia domanda. Oggi ( gennaio 2017 giugno 2019) è molto più semplice. È possibile utilizzare la nuova async/awaitsintassi . Per esempio:

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

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

Per usare async/awaitout of the box senza installazione e plugin, devi usare node-v7 o node-v8, usando il --harmonyflag.

Aggiornamento giugno 2019: utilizzando le ultime versioni di NodeJS è possibile utilizzarlo immediatamente. Non è necessario fornire argomenti da riga di comando. Anche Google Chrome lo supporta oggi.

Aggiornamento maggio 2020: Presto sarai in grado di utilizzare la awaitsintassi al di fuori di una funzione asincrona. Al livello più alto come in questo esempio

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

La proposta è nella fase 3 . Puoi usarlo oggi usando webpack 5 (alpha),

Ulteriori informazioni:


5
Penso che hai dimenticato la awaitparola chiave di frontesleep(1000)
Ryan Jarvis il

6
Questa dovrebbe davvero essere la risposta, il pacchetto sleep node.js può essere problematico.
Stuart Allen,

4
La migliore soluzione in effetti
Kostas Siabanis,

40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));Per i fanatici della linea come me :)
Predrag Stojadinović

21
let sleep = require('util').promisify(setTimeout);funziona su Nodo 7.6+ e migliora la leggibilità
BrianHVB

217

La soluzione più breve senza dipendenze:

await new Promise(resolve => setTimeout(resolve, 5000));

14
"Il massimo della raffinatezza è la semplicità." - Clare Booth Luce. Questa è di gran lunga la risposta migliore, IMO.
Arnold

3
Questo è ovviamente molto più recente delle altre risposte, ma è il più elegante del 2018 che fa il lavoro in 1 riga senza altri impatti sul codice.
Herick,

1
Questo è buono. Tuttavia, devi utilizzare NodeJS 7.6.0 o versioni successive. Non funzionerà con le versioni precedenti.
Ryan Shillington,

5
let sleep = require('util').promisify(setTimeout);ha tre caratteri più lunghi ma riutilizzabili e più leggibili imo
BrianHVB

2
Per evitare un reclamo eslint, chiamalo resolveinvece di done. Vale a direawait new Promise(resolve => setTimeout(resolve, 5000))
Darren Cook,

150

Inserire il codice che si desidera eseguire dopo il ritardo nel setTimeoutcallback:

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

2
Questa è una pratica terribilmente disordinata e generalmente cattiva, specialmente se l'OP vuole che il resto del programma venga eseguito dopo quel ritardo. Vedi la mia risposta
Elliot Bonneville,

32
@ElliotBonneville È solo un esempio per illustrare il concetto. Ovviamente potresti (dovresti) scomporre il codice in una chiamata di funzione invece di usare il codice incorporato, proprio come in qualsiasi altro posto.
JohnnyHK,

@ChristopherKemp: risulta che Node.js ha una soluzione per questo chiamata node-fibers. Controlla.
Elliot Bonneville,

Questa è un'ottima soluzione, in particolare se non si desidera eseguire alcun codice, si desidera solo imitare un metodo "sleep" all'interno di un ciclo. Niente di sbagliato in questo esempio.
Halfstop,

questo è perfetto per ritardare le richieste provenienti dal client, ho usato questo approccio per testare i miei spinner di caricamento sul lato client
moein rahimi

145

Questa è una semplice tecnica di blocco:

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

Si blocca nella misura in cui non accadrà nient'altro nella tua sceneggiatura (come i callback). Ma poiché questo è uno script per console, forse è quello che ti serve!


27
orribile o no, fa un semplice wait, si blocca e funziona per qualche scopo di test. Esattamente quello che stavo cercando.
Clijsters,

8
risponde perfettamente alla domanda senza librerie di terze parti ed è semplice. Eppure la gente dice "orribile" .... Questo è ottimo, ad esempio per simulare un pesante carico della CPU, ecc. Molto simile a questo phpied.com/sleep-in-javascript
Don Cheadle

2
Inoltre, questo è l'approccio richiesto se si sta tentando di simulare qualcosa che rallenti il ​​ciclo degli eventi, rallentando così anche altri utenti / connessioni. L'aggiunta di callback ritardati non avrà alcun impatto sugli altri utenti / connessioni.
Don Cheadle,

13
Questo è un spin-wait.
Mike Atlas

7
@Ali che sembra essere l'obiettivo.
Félix Gagnon-Grenier,

63

Sul nodo 7.6.0 o successivo

Il nodo supporta l'attesa nativa:

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

quindi se puoi usare le funzioni asincrone:

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

o:

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

Nelle versioni precedenti del nodo (risposta originale)

Volevo un sonno asincrono che funzionasse in Windows e Linux, senza eseguire il hogging della mia CPU con un lungo ciclo. Ho provato il pacchetto sleep ma non si installava sul mio box di Windows. Ho finito per usare:

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

Per installarlo, digitare:

npm install system-sleep

Nel tuo codice,

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

Funziona come un fascino.


1
ottima risposta grazie - ha reso possibile un codice impossibile - questo NON è un giro-attesa
danday74

1
Per ulteriori ricerche questo modulo si basa sul deasync che è stato biforcuto da un altro repository github deasync. Il repository originale avverte di non usarlo in quanto è un hack. Funziona ma non su tutte le piattaforme, quindi se hai bisogno di una soluzione multipiattaforma evita questa.
danday74,

1
sì, non ho mai provato i probs con esso ed è fantastico per dev - sotto il cofano credo che si basi su C o C ++ che è ampiamente disponibile ma immagino che su alcune macchine non lo sia e questo è quando fallisce, motivo per cui causa problemi - in caso di errore, la
sospensione del

1
Questo pacchetto non funziona su Mac OS, pertanto non è compatibile con la compatibilità incrociata e pertanto non è realizzabile. Vedi github.com/jochemstoel/nodejs-system-sleep/issues/4
nuzzolilo

1
@Nuzzolilo È strano. Abbiamo uno sviluppatore su Mac OS e non ha mai menzionato nulla di sbagliato. Sospetto che sia una versione specifica di Mac OS. Ho anche aggiornato questa risposta poiché sleep è sostanzialmente integrato nelle versioni più recenti di NodeJS.
Ryan Shillington,

62

Funzione sleep semplice ed elegante che utilizza Javascript moderno

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

Nessuna dipendenza, nessun inferno di richiamata; questo è tutto :-)


Considerando l'esempio fornito nella domanda, ecco come dormire tra due registri della console:

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

main();

Lo "svantaggio" è che ora deve essere asyncanche la tua funzione principale . Ma, considerando che stai già scrivendo il moderno codice Javascript, probabilmente stai (o almeno dovresti!) Usare async/ awaittutto il tuo codice, quindi questo non è davvero un problema. Tutti i browser moderni oggi lo supportano .

Fornire un po 'di comprensione della sleepfunzione per coloro che non sono abituati async/awaite per gli operatori con le frecce grasse, questo è il modo dettagliato di scriverlo:

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

L'uso dell'operatore freccia grassa, tuttavia, lo rende ancora più piccolo (e più elegante).


1
Puoi anche scrivere la funzione sleep senza asynce lasciando tutto il resto uguale. Probabilmente è più chiaro con il asyncpensiero.
Kevin Peña,

Buona cattura, @ KevinPeña. In realtà, penso di preferirlo senza async. Modificato la mia risposta con il tuo suggerimento. Non credo che renda il codice più chiaro; per quello, ricorrerei invece a JSDoc.
Lucio Paiva,

8
Mi piace avere il seguente one-liner nei miei script:const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
korya,


34

Questa domanda è piuttosto vecchia, ma recentemente V8 ha aggiunto generatori che possono realizzare ciò che l'OP ha richiesto. I generatori sono generalmente più facili da usare per le interazioni asincrone con l'assistenza di una libreria come suspend o gen-run .

Ecco un esempio usando 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');
})();

Lettura correlata (attraverso l'autopromozione spudorata): qual è il grosso problema con i generatori? .


2
Buona risposta - Ma dovrebbe leggereyield setTimeout(suspend.resume(), 10000);
edhubbell il

1
Grazie, @edhubbell. Questa risposta era basata su una versione molto vecchia di suspend, ma hai ragione riguardo all'ultima. Aggiornerò la risposta.
jmar777,

per quale versione di nodo è?
danday74,

1
@ danday74 Non riesco a ricordare esattamente quando non erano contrassegnati, ma sono in circolazione dalla v0.12 dietro la --harmonybandiera e, secondo node.green, sono almeno disponibili senza bandiere dalla v4.8.4: node.green/#ES2015-functions-generators-basic-functionality . Si noti, tuttavia, che la async/awaitsintassi più recente fornisce una soluzione migliore a questo ora, senza la necessità di librerie extra come suspend. Vedere questa risposta per un esempio: stackoverflow.com/a/41957152/376789 . Le funzioni asincrone sono disponibili (senza flag) dalla v7.10.
jmar777,

20

Su Linux / nodejs questo funziona per me:

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

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

Si sta bloccando, ma non è un circuito di attesa occupato.

Il tempo specificato è in secondi ma può essere una frazione. Non so se altri SO abbiano un comando simile.


sleepè praticamente onnipresente e utile fin dai primi giorni di UNIX en.wikipedia.org/wiki/Sleep_%28Unix%29 . Solo "non raro" os potrebbe non esistere è windows (comunque ci si potrebbe provarechild_process.spawnSync('timeout', ['/T', '10'])
umanità e

17

Se vuoi "programmare il golf" puoi fare una versione più breve di alcune delle altre risposte qui:

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

Ma davvero la risposta ideale secondo me è usare Node util libreria e la sua promisifyfunzione, che è progettata proprio per questo tipo di cose (fare versioni basate su promesse di cose non basate su promesse precedentemente esistenti):

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

In entrambi i casi puoi quindi mettere in pausa semplicemente usando awaitper chiamare la tua sleepfunzione:

await sleep(1000); // sleep for 1s/1000ms

In realtà, utilizzando util.promisify, non è possibile chiamare sleep senza fornire anche un callback. nodejs.org/api/…
Howie il

1
Ti manca il await. In un certo senso crea un callback, mette in pausa le cose e quindi riavvia il codice quando ritorna quel callback. Il callback ritorna con un valore, ma in questo caso non ci interessa, quindi non c'è niente a sinistra di await.
Machineghost il

1
scrivilo in una riga per inserire il codice golf;) const sleep = require ('util'). promisify (setTimeout);
Fabian,

14

Di recente ho creato un'astrazione più semplice chiamata wait.for per chiamare le funzioni asincrone in modalità di sincronizzazione (basata su nodi-fibre). Esiste anche una versione basata sui prossimi generatori ES6.

https://github.com/luciotato/waitfor

utilizzando wait.for , puoi chiamare qualsiasi funzione asincrona nodejs standard, come se fosse una funzione di sincronizzazione, senza bloccare il loop degli eventi del nodo.

Puoi programmare in sequenza quando ne hai bisogno, il che è (suppongo) perfetto per semplificare i tuoi script per uso personale.

usando wait.for il tuo codice sarà:

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. 

Inoltre, qualsiasi funzione asincrona può essere chiamata in modalità Sync . Controlla gli esempi.


TypeError: Object #<Object> has no method 'miliseconds'
CaffeinaAddiction

il commento dice: "// definito in waitfor / paralell-tests.js" lo prende da quel file.
Lucio M. Tato,

2
dopo averlo ricevuto wait.for/paralell-tests.jsho riscontrato un altro errore relativo a proprietà non definite ecc. Quindi ho dovuto copiarle anche io. Perché non organizzi il codice in un modo che non sarà richiesto?
user907860

Wait.for e altre soluzioni in fibra mi hanno aperto un mondo completamente nuovo! Vorrei votare questo un milione di volte se potessi. Sebbene la maggior parte della comunità di nodejs si opponga alle fibre, penso che siano un'aggiunta fantastica e sicuramente abbiano il loro posto quando si tratta di richiamare l'inferno.
Levi Roberts,

7

Poiché javascript engine (v8) esegue il codice in base alla sequenza di eventi nella coda degli eventi, non è rigoroso che javascript avvii esattamente l'esecuzione dopo il tempo specificato. Cioè, quando si impostano alcuni secondi per eseguire il codice in un secondo momento, l'attivazione del codice è puramente basata sulla sequenza nella coda degli eventi. Pertanto l'attivazione dell'esecuzione del codice può richiedere più del tempo specificato.

Quindi Node.js segue,

process.nextTick()

per eseguire il codice in un secondo momento, invece setTimeout (). Per esempio,

process.nextTick(function(){
    console.log("This will be printed later");
});

2

Con ES6 di supporto Promise, possiamo usarli senza alcun aiuto di terze parti.

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.

Quindi usalo in questo modo:

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

Si noti che la doWhatfunzione diventa il resolvecallback all'interno di new Promise(...).

Si noti inoltre che questo è sonno ASINCRONO. Non blocca il loop degli eventi. Se è necessario bloccare la sospensione, utilizzare questa libreria che realizza il blocco della sospensione con l'aiuto dei collegamenti C ++. (Sebbene la necessità di bloccare il sonno in Node come gli ambienti asincroni sia rara.)

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


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

Questo codice sopra è l'effetto collaterale della risoluzione del problema infernale di callback asincrono di Javascript. Questo è anche il motivo per cui penso che renda Javascript un linguaggio utile nel backend. In realtà questo è il miglioramento più entusiasmante introdotto nel Javascript moderno secondo me. Per comprendere appieno come funziona, è necessario comprendere appieno come funziona il generatore. La functionparola chiave seguita da a *è chiamata funzione generatore nel moderno Javascript. Il pacchetto npm ha cofornito una funzione runner per eseguire un generatore.

Essenzialmente la funzione generatore ha fornito un modo per mettere in pausa l'esecuzione di una funzione con yieldparola chiave, allo stesso tempo, yieldin una funzione generatore ha reso possibile lo scambio di informazioni tra il generatore e il chiamante. Ciò ha fornito un meccanismo per il chiamante per estrarre i dati da una promisechiamata asincrona e restituire i dati risolti al generatore. In effetti, sincronizza una chiamata asincrona.


Mentre questo codice può rispondere alla domanda, fornire un contesto aggiuntivo riguardo a come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta.
Paperino

Grazie @DonaldDuck. Il codice nella mia risposta è probabilmente la parte più eccitante del miglioramento di Javascript. Sono così stupito che alcune persone super intelligenti pensano a questo modo di risolvere il problema dell'inferno della richiamata.
Qian Chen

1

Questo è un modulo aromatizzato moment.js basato sull'approccio di blocco sporco suggerito da @ atlex2 . Utilizzare questo solo per i test .

const moment = require('moment');

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

module.exports = sleep;

0

Se devi solo sospendere a scopo di test l'attuale esecuzione del thread prova questo:

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

0

semplice aspetteremo per 5 secondi che si verifichi un evento (che sarebbe indicato dalla variabile done impostata su true da qualche altra parte nel codice) o quando scade il timeout che verificheremo ogni 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;
        }
    })();

0

Per alcune persone, la risposta accettata non funziona, ho trovato quest'altra risposta e funziona per me: come posso passare un parametro a un callback setTimeout ()?

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

'ciao' è il parametro che viene passato, è possibile passare tutti i parametri dopo il timeout. Grazie a @Fabio Phms per la risposta.


0

Invece di fare tutto questo setTimeout, sleep e molte altre cose, è meglio usare

const delay = require('delay');
await delay(2000); // will wait for 2 seconds before executing next line of code

Puoi spiegare che cos'è il "ritardo" e collegare i suoi documenti? Perché è meglio?
boycy

-2

Le altre risposte sono fantastiche, ma ho pensato di prendere un tatto diverso.

Se tutto ciò che stai veramente cercando è rallentare un file specifico in Linux:

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

nodo myprog slowfile

Questo dormirà 1 secondo ogni cinque righe. Il programma del nodo andrà lento come lo scrittore. Se sta facendo altre cose, continueranno a velocità normale.

Mkfifo crea una pipe first-in-first-out. È ciò che rende questo lavoro. La riga perl scriverà velocemente quanto vuoi. $ | = 1 dice di non bufferizzare l'output.


-3

Metto insieme, dopo aver letto le risposte in questa domanda, una semplice funzione che può anche fare una richiamata, se ne hai bisogno:

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

-4

Per maggiori informazioni su

yield sleep(2000); 

dovresti controllare Redux-Saga . Ma è specifico per la scelta di Redux come framework del modello (anche se strettamente non necessario).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.