Comment puis-je attendre dans Node.js (JavaScript)? J'ai besoin de faire une pause pendant un certain temps

386

Je développe un script de console pour des besoins personnels. Je dois pouvoir faire une pause pendant une période prolongée, mais, d'après mes recherches, Node.js n'a aucun moyen de s'arrêter comme requis. Il devient difficile de lire les informations des utilisateurs après un certain temps ... J'ai vu du code, mais je pense qu'ils doivent avoir un autre code à l'intérieur d'eux pour qu'ils fonctionnent, tels que:

    setTimeout(function() {
    }, 3000);

Cependant, j'ai besoin de tout après cette ligne de code pour exécuter après la période de temps.

Par exemple,

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

J'ai aussi vu des choses comme

    yield sleep(2000);

Mais Node.js ne le reconnaît pas.

Comment puis-je réaliser cette pause prolongée?

Christopher Allen
la source
3
comment vous marquez une de ces personnes comme réponse correcte :)
Billybonks
1
@Christopher Allen, peut-être pas pertinent, mais fait le travail: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Haitham Sweilem
Le module npm sleep-sleep pourrait faire l'affaire (cependant, je ne l'utiliserais que pour le débogage)
Julian Soro

Réponses:

212

La meilleure façon de le faire est de diviser votre code en plusieurs fonctions, comme ceci:

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

Elle est similaire à la solution de JohnnyHK , mais beaucoup plus nette et plus facile à étendre.

Elliot Bonneville
la source
7
@LucasSeveryn Vous faites alors quelque chose de mal. Il s'agit d'un modèle de conception de base.
Elliot Bonneville
Et que faites-vous lorsque le démarrage de la fonction n'est pas à une seule fonction, mais à 10 fichiers du code qui doit être désasynchronisé?
Cyril Duchon-Doris
10
@ CyrilDuchon-Doris Quoi?
AturSams
3
utilisez la réponse de @ machineghost pour une solution élégante basée sur des promesses qui ne nécessite aucun code personnalisé et prend en charge wait / async
Dane Macaulay
C'est asynchrone, cela signifie que si function1 se termine avant 3 secondes, ils commencent à se chevaucher. Ensuite, vous ne pouvez même pas revenir et ce n'est presque jamais utilisable. La seule façon que j'ai trouvée jusqu'à présent était d'utiliserdebugger;
Cristian Traìna
429

Une nouvelle réponse à une vieille question. Aujourd'hui ( janvier 2017 juin 2019), c'est beaucoup plus facile. Vous pouvez utiliser la nouvelle async/awaitsyntaxe . Par exemple:

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

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

Pour utiliser async/awaithors de la boîte sans installation et plugins, vous devez utiliser node-v7 ou node-v8, en utilisant l' --harmonyindicateur.

Mise à jour de juin 2019: en utilisant les dernières versions de NodeJS, vous pouvez l'utiliser immédiatement. Pas besoin de fournir d'arguments en ligne de commande. Même Google Chrome le prend en charge aujourd'hui.

Mise à jour de mai 2020: vous pourrez bientôt utiliser la awaitsyntaxe en dehors d'une fonction asynchrone. Au niveau supérieur comme dans cet exemple

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

La proposition est à l'étape 3 . Vous pouvez l'utiliser aujourd'hui en utilisant le webpack 5 (alpha),

Plus d'informations:

Aminadav Glickshtein
la source
5
Je pense que vous avez oublié le awaitmot - clé devantsleep(1000)
Ryan Jarvis
6
Cela devrait vraiment être la réponse, le package sleep node.js peut être gênant.
Stuart Allen
4
La meilleure solution en effet
Kostas Siabanis
40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));Pour les monstres oneliner comme moi :)
Predrag Stojadinović
21
let sleep = require('util').promisify(setTimeout);fonctionne sur Node 7.6+ et améliore la lisibilité
BrianHVB
217

La solution la plus courte sans dépendances:

await new Promise(resolve => setTimeout(resolve, 5000));
k06a
la source
14
"Le summum de la sophistication est la simplicité." - Clare Booth Luce. C'est de loin la meilleure réponse, l'OMI.
Arnold
3
C'est évidemment beaucoup plus récent que les autres réponses, mais c'est le plus élégant à partir de 2018 qui fait le travail en 1 ligne sans autres impacts sur le code.
Herick
1
C'est une bonne. Vous devez cependant utiliser NodeJS 7.6.0 ou supérieur. Cela ne fonctionnera pas sur les anciennes versions.
Ryan Shillington
5
let sleep = require('util').promisify(setTimeout);est trois caractères plus long mais imo réutilisable et plus lisible
BrianHVB
2
Pour éviter une plainte eslint, appelez-le à la resolveplace de done. C'est-à-direawait new Promise(resolve => setTimeout(resolve, 5000))
Darren Cook
150

Mettez le code que vous souhaitez exécuter après le délai dans le setTimeoutrappel:

console.log('Welcome to My Console,');
setTimeout(function() {
    console.log('Blah blah blah blah extra-blah');
}, 3000);
JohnnyHK
la source
2
C'est une pratique terriblement désordonnée et généralement mauvaise, surtout si l'OP souhaite que le reste du programme s'exécute après ce délai. Voir ma réponse.
Elliot Bonneville
32
@ElliotBonneville Ce n'est qu'un exemple pour illustrer le concept. De toute évidence, vous pourriez (devriez) factoriser le code dans un appel de fonction au lieu d'utiliser du code en ligne, comme partout ailleurs.
JohnnyHK
@ChristopherKemp: Il s'avère que Node.js a une solution pour cela appelée node-fibers. Vérifiez-le.
Elliot Bonneville
C'est une excellente solution, en particulier si vous ne voulez exécuter aucun code, vous voulez simplement imiter une méthode "sleep" à l'intérieur d'une boucle. Rien de mal à cet exemple.
Halfstop
c'est parfait pour retarder les requêtes provenant du client, j'ai utilisé cette approche pour tester mes filateurs de chargement côté client
moein rahimi
145

Il s'agit d'une technique de blocage simple:

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

C'est bloquant dans la mesure où rien d'autre ne se produira dans votre script (comme les rappels). Mais comme il s'agit d'un script de console, c'est peut-être ce dont vous avez besoin!

atlex2
la source
27
horrible ou non, il fait un simple wait, il bloque et il fonctionne à des fins de test. Exactement ce que je cherchais.
Clijsters du
8
répond parfaitement à la question sans bibliothèques tierces et est simple. Pourtant, les gens disent "horrible" .... C'est génial par exemple pour simuler une charge CPU importante, etc. Btw très similaire à ce phpied.com/sleep-in-javascript
Don Cheadle
2
C'est également l'approche requise si vous essayez de simuler quelque chose qui ralentirait la boucle d'événements, ralentissant ainsi les autres utilisateurs / connexions. L'ajout de rappels différés n'aura aucun impact sur les autres utilisateurs / connexions.
Don Cheadle
13
Ceci est un spin-wait.
Mike Atlas
7
@Ali qui semble être le but.
Félix Gagnon-Grenier
63

Sur le nœud 7.6.0 ou supérieur

Le nœud prend en charge l'attente en natif:

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

alors si vous pouvez utiliser les fonctions asynchrones:

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

ou:

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

Sur les anciennes versions de Node (réponse originale)

Je voulais un sommeil asynchrone qui fonctionnait sous Windows et Linux, sans monopoliser mon processeur avec une longue boucle while. J'ai essayé le package sleep mais il ne s'installait pas sur ma box Windows. J'ai fini par utiliser:

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

Pour l'installer, tapez:

npm install system-sleep

Dans votre code,

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

Fonctionne comme un charme.

Ryan Shillington
la source
1
excellente réponse merci - cela a rendu possible un code impossible - ce n'est PAS une attente d'attente
danday74
1
Sur des recherches plus poussées, ce module s'appuie sur deasync qui est issu d'un autre dépôt github de deasync. Le dépôt d'origine prévient de ne pas l'utiliser car il s'agit d'un hack. Cela fonctionne, mais pas sur toutes les plates-formes, donc si vous avez besoin d'une solution multiplateforme, évitez celle-ci.
danday74
1
oui, je n'ai jamais rencontré de problèmes avec lui et c'est génial pour les développeurs - sous le capot, je crois qu'il repose sur C ou C ++ qui est largement disponible, mais je suppose que sur certaines machines, ce n'est pas le cas et c'est quand il échoue, c'est pourquoi il provoque des problèmes - en cas d'échec, le sommeil du système retombe dans une attente de rotation qui gèle l'exécution du code
danday74
1
Ce package ne fonctionne pas sur Mac OS, et n'est donc pas compatible, et donc ne fonctionne pas. Voir github.com/jochemstoel/nodejs-system-sleep/issues/4
nuzzolilo
1
@Nuzzolilo C'est bizarre. Nous avons un développeur sur Mac OS et il n'a jamais rien dit de mal. Je soupçonne que c'est une version spécifique de Mac OS. J'ai également mis à jour cette réponse car le sommeil est essentiellement intégré aux nouvelles versions de NodeJS.
Ryan Shillington
62

Fonction de sommeil simple et élégante utilisant Javascript moderne

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

Pas de dépendances, pas d'enfer de rappel; c'est ça :-)


Compte tenu de l'exemple donné dans la question, voici comment nous dormirions entre deux journaux de console:

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

main();

L'inconvénient est que votre fonction principale doit désormais l'être asyncégalement. Mais, étant donné que vous écrivez déjà du code Javascript moderne, vous utilisez probablement (ou du moins devrait l'être!) En utilisant async/await partout dans votre code, donc ce n'est vraiment pas un problème. Tous les navigateurs modernes le supportent aujourd'hui .

Donner un petit aperçu de la sleepfonction pour ceux qui n'y sont pas habitués async/awaitet des gros opérateurs de flèches, voici la façon verbeuse de l'écrire:

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

Cependant, l'utilisation de l'opérateur de flèche grasse le rend encore plus petit (et plus élégant).

Lucio Paiva
la source
1
Vous pouvez également écrire la fonction de veille sans asyncet en laissant tout le reste inchangé. C'est probablement plus clair avec le asyncbien.
Kevin Peña
Bonne prise, @ KevinPeña. En fait, je pense que je le préfère sans async. Modifié ma réponse avec votre suggestion. Je ne pense pas que cela rend le code plus clair; pour cela, je recourirais plutôt à JSDoc.
Lucio Paiva
8
J'aime avoir le one-liner suivant dans mes scripts:const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
korya
46

Vous pouvez utiliser ce www.npmjs.com/package/sleep

var sleep = require('sleep');
sleep.sleep(10); // sleep for ten seconds
Aleksej
la source
4
Cela fonctionne bien MacOS, mais il rencontre des erreurs en CentOSraison d' erreurs node_gyp . Cela ne semble pas portable.
AechoLiu
1
peut-être un problème causé non pas par le système d'exploitation, mais par la construction du nœud
Aleksej
34

Cette question est assez ancienne, mais récemment V8 a ajouté des générateurs qui peuvent accomplir ce que l'OP a demandé. Les générateurs sont généralement plus faciles à utiliser pour les interactions asynchrones avec l'aide d'une bibliothèque telle que la suspension ou la génération .

Voici un exemple d'utilisation de la suspension:

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

Lecture connexe (par le biais d'une auto-promotion éhontée): Quel est le gros problème avec les générateurs? .

jmar777
la source
2
Bonne réponse - mais devrait lireyield setTimeout(suspend.resume(), 10000);
edhubbell
1
Merci, @edhubbell. Cette réponse était basée sur une très ancienne version de suspend, mais vous avez raison concernant la dernière. Je mettrai à jour la réponse.
jmar777
à quelle version de nœud est-ce destiné?
danday74
1
@ danday74 Je ne me souviens pas exactement quand ils ont été non marqués, mais ils existent depuis la v0.12 derrière le --harmonydrapeau, et selon node.green, ils sont au moins disponibles sans drapeau depuis la v4.8.4: node.green/#ES2015-functions-generators-basic-functionality . Veuillez noter, cependant, que la nouvelle async/awaitsyntaxe fournit une meilleure solution maintenant, sans avoir besoin de bibliothèques supplémentaires comme suspend. Voir cette réponse pour un exemple: stackoverflow.com/a/41957152/376789 . Les fonctions asynchrones sont disponibles (sans drapeaux) depuis la v7.10.
jmar777
20

Sur Linux / nodejs, cela fonctionne pour moi:

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

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

Il bloque, mais ce n'est pas une boucle d'attente occupée.

Le temps que vous spécifiez est en secondes mais peut être une fraction. Je ne sais pas si d'autres OS ont une commande similaire.

Bart Verheijen
la source
sleepest à peu près omniprésent et un tout autour depuis les premiers jours UNIX en.wikipedia.org/wiki/Sleep_%28Unix%29 . Seulement « pas rare » os il peut - être pas exister est Windows (mais il vous pourriez essayerchild_process.spawnSync('timeout', ['/T', '10'])
humanityANDpeace
17

Si vous voulez "coder le golf", vous pouvez faire une version plus courte de certaines des autres réponses ici:

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

Mais vraiment la réponse idéale à mon avis est d'utiliser la utilbibliothèque de Node et sa promisifyfonction, qui est conçue exactement pour ce genre de chose (faire des versions basées sur des promesses de choses non basées sur des promesses):

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

Dans les deux cas, vous pouvez ensuite faire une pause simplement en utilisant awaitpour appeler votre sleepfonction:

await sleep(1000); // sleep for 1s/1000ms
machineghost
la source
En fait, en utilisant util.promisify, il n'est pas possible d'appeler sleep sans fournir également un rappel. nodejs.org/api/…
Howie
1
Vous manquez le await. Il crée en quelque sorte un rappel, suspend les choses, puis redémarre le code lorsque ce rappel revient. Le rappel revient avec une valeur, mais nous ne nous en soucions pas dans ce cas, il n'y a donc rien à gauche de await.
machineghost
1
écrivez-le sur une ligne pour mettre le code golf;) const sleep = require ('util'). promisify (setTimeout);
Fabian
14

J'ai récemment créé une abstraction plus simple appelée wait.for pour appeler des fonctions asynchrones en mode sync (basé sur des fibres nodales ). Il existe également une version basée sur les générateurs ES6 à venir.

https://github.com/luciotato/waitfor

En utilisant wait.for , vous pouvez appeler n'importe quelle fonction asynchrone nodejs standard, comme s'il s'agissait d'une fonction de synchronisation, sans bloquer la boucle d'événements du node.

Vous pouvez coder séquentiellement quand vous en avez besoin, ce qui est (je suppose) parfait pour simplifier vos scripts pour un usage personnel.

en utilisant wait.for votre code sera:

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. 

Toute fonction asynchrone peut également être appelée en mode Sync . Vérifiez les exemples.

Lucio M. Tato
la source
TypeError: Object #<Object> has no method 'miliseconds'
CaffeineAddiction
le commentaire dit: "// défini dans waitfor / paralell-tests.js", récupérez-le dans ce fichier.
Lucio M. Tato
2
après l'avoir obtenu, wait.for/paralell-tests.jsj'ai rencontré une autre erreur liée à des propriétés non définies, etc. J'ai donc dû les copier aussi. Pourquoi n'organisez-vous pas le code de manière à ce que cela ne soit pas nécessaire?
user907860
Wait.for et d'autres solutions de fibre m'ont ouvert un tout nouveau monde! Je voterais un million de fois si je le pouvais. Bien que la plupart de la communauté nodejs s'oppose aux fibres, je pense qu'elles sont un ajout fantastique et ont certainement leur place en matière d'enfer.
Levi Roberts
7

Depuis, le moteur javascript (v8) exécute du code basé sur la séquence d'événements dans la file d'attente des événements, il n'y a pas de stricte que javascript déclenche exactement l'exécution après l'heure spécifiée. Autrement dit, lorsque vous définissez quelques secondes pour exécuter le code ultérieurement, le code de déclenchement est purement basé sur la séquence dans la file d'attente d'événements. Le déclenchement de l'exécution du code peut donc prendre plus de temps que spécifié.

Alors Node.js suit,

process.nextTick()

pour exécuter le code plus tard à la place setTimeout (). Par exemple,

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

Avec ES6 prenant en charge Promises, nous pouvons les utiliser sans aucune aide tierce.

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.

Ensuite, utilisez-le comme ceci:

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

Notez que la doWhatfonction devient le resolverappel dans le new Promise(...).

Notez également qu'il s'agit d'un sommeil ASYNCHRONE. Il ne bloque pas la boucle d'événements. Si vous avez besoin de bloquer le sommeil, utilisez cette bibliothèque qui réalise le blocage du sommeil à l'aide de liaisons C ++. (Bien que le besoin d'un sommeil bloquant dans les environnements Node comme async soit rare.)

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

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

Ce code ci-dessus est l'effet secondaire de la résolution du problème d'enfer de rappel asynchrone de Javascript. C'est aussi la raison pour laquelle je pense que Javascript est un langage utile dans le backend. En fait, c'est à mon avis l'amélioration la plus excitante introduite dans le Javascript moderne. Pour bien comprendre comment cela fonctionne, le fonctionnement du générateur doit être parfaitement compris. Le functionmot-clé suivi d'un *est appelé une fonction de générateur dans Javascript moderne. Le package npm cofournit une fonction runner pour exécuter un générateur.

La fonction essentiellement générateur a fourni un moyen de suspendre l'exécution d'une fonction avec un yieldmot clé, en même temps,yield dans une fonction de générateur a permis d'échanger des informations entre l'intérieur du générateur et l'appelant. Cela a fourni un mécanisme permettant à l'appelant d'extraire des données d'un promiseappel asynchrone et de renvoyer les données résolues au générateur. En effet, il rend un appel asynchrone synchrone.

Qian Chen
la source
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la manière et / ou la raison pour laquelle il résout le problème améliorerait la valeur à long terme de la réponse.
Donald Duck
Merci @DonaldDuck. Le code dans ma réponse est probablement la partie la plus excitante de l'amélioration de Javascript. Je suis tellement étonné que certaines personnes super intelligentes réfléchissent à cette façon de résoudre le problème de l'enfer de rappel.
Qian Chen
1

Il s'agit d'un module aromatisé moment.js basé sur l' approche de blocage sale suggérée par @ atlex2 . Utilisez-le uniquement pour les tests .

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
la source
0

Si vous avez juste besoin de suspendre à des fins de test votre exécution actuelle du thread, essayez ceci:

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
la source
0

simple, nous allons attendre 5 secondes pour qu'un événement se produise (cela serait indiqué par une variable done définie sur true ailleurs dans le code) ou lorsque le délai expirera que nous vérifierons toutes les 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
la source
0

Pour certaines personnes, la réponse acceptée ne fonctionne pas, j'ai trouvé cette autre réponse et elle fonctionne pour moi: comment puis-je passer un paramètre à un rappel setTimeout ()?

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

'bonjour' est le paramètre transmis, vous pouvez passer tous les paramètres après le délai d'expiration. Merci à @Fabio Phms pour la réponse.

Dazt
la source
0

Au lieu de faire tous ces setTimeout, sleep et bien d'autres choses, il vaut mieux utiliser

const delay = require('delay');
await delay(2000); // will wait for 2 seconds before executing next line of code
Rishab
la source
Pouvez-vous expliquer ce qu'est le «retard» et créer un lien vers ses documents? Pourquoi est-ce mieux?
boycy
-2

Les autres réponses sont excellentes mais j'ai pensé que je prendrais un tact différent.

Si tout ce que vous recherchez est de ralentir un fichier spécifique sous Linux:

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

nœud myprog slowfile

Cela dormira 1 seconde toutes les cinq lignes. Le programme de nœud ira aussi lentement que l'écrivain. S'il fait autre chose, il continuera à vitesse normale.

Le mkfifo crée un tuyau premier entré, premier sorti. C'est ce qui fait que ça marche. La ligne Perl écrira aussi vite que vous le souhaitez. Le $ | = 1 dit de ne pas mettre en tampon la sortie.

Pete
la source
-3

J'ai mis en place, après avoir lu les réponses à cette question, une fonction simple qui peut également faire un rappel, si vous en avez besoin:

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

Pour plus d'informations sur

yield sleep(2000); 

vous devriez vérifier Redux-Saga . Mais il est spécifique à votre choix de Redux comme cadre de modèle (bien que strictement non nécessaire).

Arseni Buinitski
la source