Existe-t-il un meilleur moyen de concevoir un sleep
en JavaScript par rapport à la fonction pausecomp
( tirée d'ici )?
function pausecomp(millis)
{
var date = new Date();
var curDate = null;
do { curDate = new Date(); }
while(curDate-date < millis);
}
Ce n'est pas une copie de Veille en JavaScript - délai entre les actions ; Je veux un sommeil réel au milieu d'une fonction, et pas un délai avant qu'un morceau de code ne soit exécuté.
Depuis 2009, date à laquelle cette question a été posée, JavaScript a considérablement évolué. Toutes les autres réponses sont maintenant obsolètes ou trop compliquées. Voici la meilleure pratique actuelle:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function demo() {
console.log('Taking a break...');
await sleep(2000);
console.log('Two seconds later, showing sleep in a loop...');
// Sleep in loop
for (let i = 0; i < 5; i++) {
if (i === 3)
await sleep(2000);
console.log(i);
}
}
demo();
await sleep(<duration>)
.Notez que,
await
ne peut être exécuté que dans des fonctions précédées du mot clé async
ou du niveau supérieur de votre script dans certains environnements (par exemple, la console Chrome DevTools ou Runkit).await
met uniquement en pause la fonction async
en coursDeux nouvelles fonctionnalités JavaScript ont aidé à écrire cette fonction "sommeil":
async/await
permet au code d'attendre explicitement la promesse de règlement (résolution ou rejet).async
name __/await
atterri dans la V8 et a été activé par défaut depuis Chrome 55 (publié en décembre 2016) Si, pour une raison quelconque, vous utilisez un noeud de plus de 7 ans (qui a atteint fin de vie ) ou si vous ciblez d'anciens navigateurs, async
name __/await
peut toujours être utilisé via Babel = (un outil qui va transpiler JavaScript + nouvelles fonctionnalités dans du vieux JavaScript ordinaire), avec le plugin transform-async-to-generator
.
(Voir le réponse mise à jour pour 2016 )
Je pense qu'il est parfaitement raisonnable de vouloir effectuer une action, attendre, puis effectuer une autre action. Si vous êtes habitué à écrire dans des langages multithreads, vous avez probablement l’idée de céder l’exécution pendant un certain temps, jusqu’à ce que votre thread se réveille.
Le problème ici est que JavaScript est un modèle basé sur des événements à thread unique. Dans certains cas, il peut être intéressant d’attendre quelques secondes avec l’ensemble du moteur, mais c’est en général une mauvaise pratique. Supposons que je veuille utiliser vos fonctions pour écrire les miennes? Quand j'ai appelé votre méthode, mes méthodes gèlent. Si JavaScript pouvait en quelque sorte préserver le contexte d'exécution de votre fonction, le stocker quelque part, puis le ramener et le poursuivre plus tard, un sommeil pourrait se produire, mais il s'agirait essentiellement d'un thread.
Donc, vous êtes à peu près coincé avec ce que d'autres ont suggéré - vous devrez diviser votre code en plusieurs fonctions.
Votre question est un peu un faux choix, alors. Il n’ya aucun moyen de dormir comme vous le souhaitez et vous ne devez pas non plus rechercher la solution que vous suggérez.
En JavaScript, je réécris chaque fonction pour qu'elle se termine le plus rapidement possible. Vous voulez que le navigateur reprenne le contrôle afin qu'il puisse apporter vos modifications DOM.
Chaque fois que j'ai voulu dormir au milieu de ma fonction, j'ai refactoré l'utilisation d'une setTimeout()
.
Je vais éditer cette réponse car j'ai trouvé cela utile:
La fonction tristement célèbre du sommeil ou du retardement dans une langue est très controversée. Certains diront qu'il devrait toujours y avoir un signal ou un rappel pour déclencher une fonctionnalité donnée, d'autres argueront du fait qu'un délai arbitraire est utile. Je dis cela à chacun et une règle ne peut jamais rien dicter dans cette industrie.
L'écriture d'une fonction de sommeil est simple et rendue encore plus utilisable avec JavaScript Promises:
// sleep time expects milliseconds
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
// Usage!
sleep(500).then(() => {
// Do something after the sleep!
});
niquement pour debug/dev, je poste ceci si cela est utile à quelqu'un
Des choses intéressantes, dans Firebug (et probablement d'autres consoles js), rien ne se passe après avoir tapé, seulement après la durée de sommeil spécifiée (...)
function sleepFor( sleepDuration ){
var now = new Date().getTime();
while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }
}
Exemple d'utilisation:
function sleepThenAct(){ sleepFor(2000); console.log("hello js sleep !"); }
Je suis d'accord avec les autres affiches, un sommeil occupé n'est qu'une mauvaise idée.
Cependant, setTimeout ne retarde pas l'exécution, il exécute la ligne suivante de la fonction immédiatement après que le délai a été défini, et non pas après l'expiration du délai, de sorte que la tâche ne soit pas accomplie de la même manière qu'un sommeil.
La manière de le faire est de décomposer votre fonction en parties avant et après.
function doStuff()
{
//do some things
setTimeout(continueExecution, 10000) //wait ten seconds before continuing
}
function continueExecution()
{
//finish doing things after the pause
}
Assurez-vous que les noms de vos fonctions décrivent toujours avec précision le rôle de chaque élément (I.E. GatherInputThenWait et CheckInput, plutôt que funcPart1 et funcPart2).
Éditer
Cette méthode permet de ne pas exécuter les lignes de code que vous décidez après la fin du délai imparti, tout en rendant le contrôle au PC client pour qu'il exécute ce qu'il a mis en file d'attente.
autre édition
Comme indiqué dans les commentaires, cela ne fonctionnera absolument pas en boucle. Vous pourriez faire du piratage de fantaisie (laid) pour le faire fonctionner en boucle, mais en général, cela ne fera que créer un code spaghetti désastreux.
Pour l'amour de $ DEITY, veuillez ne pas créer de fonction sommeil occupé. setTimeout
et setInterval
font tout ce dont vous avez besoin.
Je sais que c'est une question un peu ancienne, mais si (comme moi) vous utilisez Javascript avec Rhino, vous pouvez utiliser ...
try
{
Java.lang.Thread.sleep(timeInMilliseconds);
}
catch (e)
{
/*
* This will happen if the sleep is woken up - you might want to check
* if enough time has passed and sleep again if not - depending on how
* important the sleep time is to you.
*/
}
Si vous utilisez jQuery, quelqu'un a en fait créé un plugin "delay" qui n'est rien d'autre qu'un wrapper pour setTimeout:
// Delay Plugin for jQuery
// - http://www.evanbot.com
// - © 2008 Evan Byrne
jQuery.fn.delay = function(time,func){
this.each(function(){
setTimeout(func,time);
});
return this;
};
Vous pouvez ensuite simplement l'utiliser dans une rangée d'appels de fonction comme prévu:
$('#warning')
.addClass('highlight')
.delay(1000)
.removeClass('highlight');
J'ai aussi cherché une solution de sommeil (pas de code de production, seulement de dev/tests) et trouvé cet article:
http://narayanraman.blogspot.com/2005/12/javascript-sleep-or-wait.html
... et voici un autre lien avec les solutions côté client: http://www.devcheater.com/
En outre, lorsque vous appelez alert()
, votre code sera également mis en pause, tandis que alert est affiché - vous devez trouver un moyen de ne pas afficher d'alerte, mais d'obtenir le même effet. :)
Voici. Comme le code l'indique, ne soyez pas un mauvais développeur et utilisez-le sur des sites Web. C'est une fonction d'utilité de développement.
// Basic sleep function based on ms.
// DO NOT USE ON PUBLIC FACING WEBSITES.
function sleep(ms) {
var unixtime_ms = new Date().getTime();
while(new Date().getTime() < unixtime_ms + ms) {}
}
Voici une solution simple utilisant un XMLHttpRequest synchrone:
function sleep(n){
var request = new XMLHttpRequest();
request.open('GET', '/sleep.php?n=' + n, false); // `false` makes the request synchronous
request.send(null);
}
contenu de sleep.php:
<?php sleep($_GET['n']);
Maintenant appelez-le avec: sleep (5);
Premier:
Définissez une fonction que vous souhaitez exécuter comme ceci:
function alertWorld(){
alert("Hello World");
}
Puis planifiez son exécution avec la méthode setTimeout:
setTimeout(alertWorld,1000)
Notez deux choses
Personnellement, j'aime le simple:
function sleep(seconds){
var waitUntil = new Date().getTime() + seconds*1000;
while(new Date().getTime() < waitUntil) true;
}
puis:
sleep(2); // Sleeps for 2 seconds
Je l'utilise tout le temps pour créer un temps de chargement factice lors de la création de scripts dans P5js
Une meilleure solution pour que les choses ressemblent à ce que la plupart des gens veulent, c'est utiliser une fonction anonyme:
alert('start');
var a = 'foo';
//lots of code
setTimeout(function(){ //Beginning of code that should run AFTER the timeout
alert(a);
//lots more code
},5000); // put the timeout here
C’est probablement ce qui se rapproche le plus de ce que vous obtiendrez.
Notez que si vous avez besoin de plusieurs périodes de sommeil, cela peut vite devenir très laid et vous devrez peut-être repenser votre conception.
J'encapsulerais setTimeOut dans une promesse de cohérence du code avec d'autres tâches asynchrones: Démo dans Fiddle
function sleep(ms)
{
return(new Promise(function(resolve, reject) {
setTimeout(function() { resolve(); }, ms);
}));
}
Utilisé comme ça:
sleep(2000).then(function() {
// Do something
});
Il est facile de retenir la syntaxe si vous utilisiez Promises.
La plupart des réponses ici sont erronées ou à tout le moins dépassées. Il n'y a aucune raison pour que javascript soit à thread unique, et en effet ce n'est pas le cas. Aujourd'hui, tous les navigateurs grand public prennent en charge les travailleurs, avant que cela ne soit le cas d'autres exécutions javascript telles que Rhino et Node.j prennent en charge le multithreading.
'Javascript is single threaded' n'est pas une réponse valide. Par exemple, exécuter une fonction de veille dans un poste de travail ne bloquerait aucun des codes exécutés dans le thread ui.
Dans les exécutions plus récentes prenant en charge les générateurs et le rendement, on pourrait apporter une fonctionnalité similaire à la fonction de veille dans un environnement threadé unique:
// This is based on the latest ES6 drafts.
// js 1.7+ (SpiderMonkey/Firefox 2+) syntax is slightly different
// run code you want to sleep here (ommit star if using js 1.7)
function* main(){
for (var i = 0; i < 10; i++) {
// to sleep for 10 milliseconds 10 times in a row
yield 10;
}
yield 5;
console.log('I just slept 5 milliseconds!');
}
// resume the given generator after ms milliseconds
function resume(ms, generator){
setTimeout(function(){
// ommit .value if using js 1.7
var nextSleep = generator.next().value;
resume(nextSleep, generator);
}, ms);
}
// initialize generator and get first sleep for recursive function
var
generator = main(),
firstSleep = generator.next().value;
// initialize recursive resume function
resume(firstSleep, generator);
Cette imitation du sommeil diffère d'une vraie fonction de sommeil car elle ne bloque pas le fil. C'est simplement du sucre en plus de la fonction setTimeout actuelle de javascript. Ce type de fonctionnalité a été implémenté dans Task.js et devrait fonctionner aujourd'hui dans Firefox.
Pour les navigateurs, je conviens que setTimeout et setInterval sont la voie à suivre.
Mais pour le code côté serveur, cela peut nécessiter une fonction de blocage (par exemple, vous pouvez donc effectivement avoir la synchronisation des threads).
Si vous utilisez node.js et meteor, vous avez peut-être rencontré les limites de l'utilisation de setTimeout dans une fibre. Voici le code pour la mise en veille côté serveur.
var Fiber = require('fibers');
function sleep(ms) {
var fiber = Fiber.current;
setTimeout(function() {
fiber.run();
}, ms);
Fiber.yield();
}
Fiber(function() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}).run();
console.log('back in main');
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
J'ai cherché/cherché plusieurs pages Web sur javascript veille/attente ... et il n'y a pas de réponse si vous voulez utiliser javascript pour "RUN, DELAY, RUN" ... ce que la plupart des gens ont eu soit, "RUN, RUN (inutile trucs), RUN "ou" RUN, RUN + RUN retardé "....
J'ai donc mangé des hamburgers et j'ai réfléchi ::: voici une solution qui fonctionne ... mais vous devez modifier vos codes d'exécution ... ::: oui, je sais, c'est simplement une refactorisation plus facile à lire .. . encore...
//......................................... //Exemple 1:
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setInterval
var i = 0;
function run() {
//pieces of codes to run
if (i==0){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i==1){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i==2){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i >2){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i==5){document.getElementById("id1").innerHTML= "<p>all code segment finished running</p>"; clearInterval(t); } //end interval, stops run
i++; //segment of code finished running, next...
}
run();
t=setInterval("run()",1000);
</script>
</body>
</html>
// .................................... // exemple2:
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setTimeout
var i = 0;
function run() {
//pieces of codes to run, can use switch statement
if (i==0){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>"; sleep(1000);}
if (i==1){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>"; sleep(2000);}
if (i==2){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>"; sleep(3000);}
if (i==3){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>";} //stops automatically
i++;
}
function sleep(dur) {t=setTimeout("run()",dur);} //starts flow control again after dur
run(); //starts
</script>
</body>
</html>
// ................. exemple3:
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setTimeout
var i = 0;
function flow() {
run(i);
i++; //code segment finished running, increment i; can put elsewhere
sleep(1000);
if (i==5) {clearTimeout(t);} //stops flow, must be after sleep()
}
function run(segment) {
//pieces of codes to run, can use switch statement
if (segment==0){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==1){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment >2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
}
function sleep(dur) {t=setTimeout("flow()",dur);} //starts flow control again after dur
flow(); //starts flow
</script>
</body>
</html>
// .............. exemple4:
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setTimeout, switch
var i = 0;
function flow() {
switch(i)
{
case 0:
run(i);
sleep(1000);
break;
case 1:
run(i);
sleep(2000);
break;
case 5:
run(i);
clearTimeout(t); //stops flow
break;
default:
run(i);
sleep(3000);
break;
}
}
function run(segment) {
//pieces of codes to run, can use switch statement
if (segment==0){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==1){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment >2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
i++; //current segment of code finished running, next...
}
function sleep(dur) {t=setTimeout("flow()",dur);} //starts flow control again after dur
flow(); //starts flow control for first time...
</script>
</body>
</html>
Beaucoup de réponses ne répondent pas (directement) à la question, et celle-ci non plus ...
Voici mes deux cents (ou fonctions):
Si vous voulez des fonctions moins encombrantes que setTimeout
et setInterval
, vous pouvez les encapsuler dans des fonctions qui inversent simplement l'ordre des arguments et leur attribuez de jolis noms:
function after(ms, fn){ setTimeout(fn, ms); }
function every(ms, fn){ setInterval(fn, ms); }
Versions CoffeeScript:
after = (ms, fn)-> setTimeout fn, ms
every = (ms, fn)-> setInterval fn, ms
Vous pouvez ensuite les utiliser correctement avec des fonctions anonymes:
after(1000, function(){
console.log("it's been a second");
after(1000, function(){
console.log("it's been another second");
});
});
Maintenant, il se lit facilement comme "après N millisecondes, ..." (ou "toutes les N millisecondes, ...")
Un scénario dans lequel vous voudriez peut-être une fonction sleep () plutôt que setTimeout () est si vous avez une fonction répondant à un clic de l'utilisateur qui finira par ouvrir une nouvelle fenêtre contextuelle et que vous avez lancé un traitement nécessitant une courte période à compléter avant que la popup ne s’affiche. Le fait de déplacer la fenêtre ouverte dans une fermeture signifie qu’elle est généralement bloquée par le navigateur.
Je peux comprendre l'objectif d'une fonction de veille si vous devez gérer une exécution synchrone. Les fonctions setInterval et setTimeout créent un thread d'exécution parallèle qui renvoie la séquence d'exécution au programme principal, ce qui est inefficace si vous devez attendre un résultat donné. Bien sûr, on peut utiliser des événements et des gestionnaires, mais dans certains cas ce n’est pas ce qui est prévu.
Cela peut être fait en utilisant la méthode sleep de Java. Je l'ai testé sous FF et IE et il ne verrouille pas l'ordinateur, ne gaspille pas de ressources et ne provoque pas de problèmes de serveurs sans fin. Cela me semble une solution propre.
Vous devez d’abord charger Java sur la page et rendre ses méthodes disponibles. Pour ce faire, j'ai fait ceci:
<html>
<head>
<script type="text/javascript">
function load() {
var appletRef = document.getElementById("app");
window.Java = appletRef.Packages.Java;
} // endfunction
</script>
<body onLoad="load()">
<embed id="app" code="Java.applet.Applet" type="application/x-Java-applet" MAYSCRIPT="true" width="0" height="0" />
Ensuite, tout ce que vous avez à faire lorsque vous souhaitez une pause sans douleur dans votre JS est:
Java.lang.Thread.sleep(xxx)
Où xxx est le temps en millisecondes. Dans mon cas (à titre de justification), cela faisait partie de l'exécution des commandes dorsales dans une très petite entreprise et je devais imprimer une facture qui devait être chargée à partir du serveur. Je l'ai fait en chargeant la facture (en tant que page Web) dans un iFrame, puis en imprimant l'iFrame. Bien sûr, je devais attendre que la page soit entièrement chargée avant de pouvoir imprimer. Le JS a donc dû faire une pause. Pour ce faire, la page de facture (dans iFrame) a modifié un champ de formulaire masqué de la page parent avec l'événement onLoad. Et le code sur la page parente pour imprimer la facture ressemblait à ceci (des parties non pertinentes ont été coupées pour plus de clarté):
var isReady = eval('document.batchForm.ready');
isReady.value=0;
frames['rpc_frame'].location.href=url;
while (isReady.value==0) {
Java.lang.Thread.sleep(250);
} // endwhile
window.frames['rpc_frame'].focus();
window.frames['rpc_frame'].print();
Pour que l'utilisateur appuie sur le bouton, le script charge la page de facture, attend, vérifie tous les quarts de secondes pour voir si le chargement de la page de facture est terminé, puis affiche la boîte de dialogue Imprimer pour que l'utilisateur l'envoie à l'imprimante. QED.
Ajout de mes deux bits. J'avais besoin d'une attente chargée pour les tests. Je ne voulais pas scinder le code car ce serait beaucoup de travail, alors un simple pour le faire pour moi.
for (var i=0;i<1000000;i++){
//waiting
}
Je ne vois pas d'inconvénient à le faire et cela a été efficace pour moi.
Vous ne pouvez pas dormir comme ça en JavaScript, ou plutôt, vous ne devriez pas. L'exécution d'une veille ou d'une boucle while provoquera le blocage du navigateur de l'utilisateur jusqu'à la fin de la boucle.
Utilisez une minuterie, comme spécifié dans le lien que vous avez référencé.
Pour le cas particulier de vouloir espacer un ensemble d'appels exécutés par une boucle, vous pouvez utiliser quelque chose comme le code ci-dessous avec prototype. Sans prototype, vous pouvez remplacer la fonction delay par setTimeout.
function itemHandler(item)
{
alert(item);
}
var itemSet = ['a','b','c'];
// Each call to itemHandler will execute
// 1 second apart
for(var i=0; i<itemSet.length; i++)
{
var secondsUntilExecution = i;
itemHandler.delay(secondsUntilExecution, item)
}
Si vous êtes sur node.js, vous pouvez jeter un oeil à fibres - une extension C native à node, une simulation de type multi-threading.
Cela vous permet de faire un sleep
réel qui bloque l'exécution dans une fibre, mais qui ne bloque pas le thread principal et les autres fibres.
Voici un exemple fraîchement sorti de leur propre readme:
// sleep.js
var Fiber = require('fibers');
function sleep(ms) {
var fiber = Fiber.current;
setTimeout(function() {
fiber.run();
}, ms);
Fiber.yield();
}
Fiber(function() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}).run();
console.log('back in main');
- et les résultats sont:
$ node sleep.js
wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
Une vieille question de 2009. Maintenant, en 2015, une nouvelle solution est possible avec les générateurs définis dans ECMAscript 2015, également appelé ES6. Il a été approuvé en juin, mais il a déjà été implémenté dans Firefox et Chrome. Désormais, une fonction de veille peut être rendue non occupée, non bloquante et imbriquée dans des boucles internes et des sous-fonctions sans geler le navigateur. Seul du JavaScript pur est nécessaire, pas de bibliothèques ni de frameworks.
Le programme ci-dessous montre comment sleep()
et runSleepyTask()
peuvent être créés. La fonction sleep()
est uniquement une instruction yield
. C'est tellement simple qu'il est en fait plus facile d'écrire l'instruction yield
directement au lieu d'appeler sleep()
, mais il n'y aurait alors pas de mot de sommeil :-) Le rendement renvoie une valeur temporelle à la méthode next()
dans wakeup()
et waits. Le "sommeil" réel est fait dans wakeup()
en utilisant le bon vieux setTimeout()
. Au rappel, la méthode next()
déclenche la poursuite de l'instruction yield
et la "magie" du rendement est que toutes les variables locales et la pile d'appels qui l'entoure sont toujours intactes.
Les fonctions qui utilisent sleep () ou yield doivent être définies en tant que générateurs. C'est facile en ajoutant un astérisque au mot clé function*
. Exécuter un générateur est un peu plus compliqué. Lorsqu'il est appelé avec le mot clé new
, le générateur renvoie un objet comportant la méthode next()
, mais le corps du générateur n'est pas exécuté (le mot clé new
est facultatif et ne fait aucune différence). La méthode next()
déclenche l'exécution du corps du générateur jusqu'à ce qu'il rencontre un yield
. La fonction wrapper runSleepyTask()
démarre le ping-pong: next()
attend un yield
et yield
attend un next()
.
Un autre moyen d'invoquer un générateur est d'utiliser le mot clé yield*
. Ici, il fonctionne comme un simple appel de fonction, mais il inclut également la possibilité de renvoyer à next()
.
Tout cela est démontré par l'exemple drawTree()
. Il dessine un arbre avec des feuilles sur une scène 3D en rotation. Un arbre est dessiné comme un tronc avec 3 parties au sommet dans différentes directions. Chaque partie est ensuite dessinée sous la forme d'une autre mais plus petite arborescence en appelant drawTree()
de manière récursive après un court sommeil. Un très petit arbre est dessiné comme une feuille.
Chaque feuille a sa propre vie dans une tâche distincte lancée avec runSleepyTask()
. Il naît, grandit, s'assied, se fane, tombe et meurt dans growLeaf()
. La vitesse est contrôlée avec sleep()
. Cela montre à quel point le multitâche peut être simple.
function* sleep(milliseconds) {yield milliseconds};
function runSleepyTask(task) {
(function wakeup() {
var result = task.next();
if (!result.done) setTimeout(wakeup, result.value);
})()
}
//////////////// written by Ole Middelboe /////////////////////////////
pen3D =setup3D();
var taskObject = new drawTree(pen3D.center, 5);
runSleepyTask(taskObject);
function* drawTree(root3D, size) {
if (size < 2) runSleepyTask(new growLeaf(root3D))
else {
pen3D.drawTrunk(root3D, size);
for (var p of [1, 3, 5]) {
var part3D = new pen3D.Thing;
root3D.add(part3D);
part3D.move(size).turn(p).tilt(1-p/20);
yield* sleep(50);
yield* drawTree(part3D, (0.7+p/40)*size);
}
}
}
function* growLeaf(stem3D) {
var leaf3D = pen3D.drawLeaf(stem3D);
for (var s=0;s++<15;) {yield* sleep(100); leaf3D.scale.multiplyScalar(1.1)}
yield* sleep( 1000 + 9000*Math.random() );
for (var c=0;c++<30;) {yield* sleep(200); leaf3D.skin.color.setRGB(c/30, 1-c/40, 0)}
for (var m=0;m++<90;) {yield* sleep( 50); leaf3D.turn(0.4).tilt(0.3).move(2)}
leaf3D.visible = false;
}
///////////////////////////////////////////////////////////////////////
function setup3D() {
var scene, camera, renderer, diretionalLight, pen3D;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75,
window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 15, 20);
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
directionalLight = new THREE.DirectionalLight(0xffffaa, 0.7);
directionalLight.position.set(-1, 2, 1);
scene.add(directionalLight);
scene.add(new THREE.AmbientLight(0x9999ff));
(function render() {
requestAnimationFrame(render);
// renderer.setSize( window.innerWidth, window.innerHeight );
scene.rotateY(10/60/60);
renderer.render(scene, camera);
})();
window.addEventListener(
'resize',
function(){
renderer.setSize( window.innerWidth, window.innerHeight );
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
},
false
);
pen3D = {
drawTrunk: function(root, size) {
// root.skin = skin(0.5, 0.3, 0.2);
root.add(new THREE.Mesh(new THREE.CylinderGeometry(size/12, size/10, size, 16),
root.skin).translateY(size/2));
root.add(new THREE.Mesh(new THREE.SphereGeometry(size/12, 16),
root.skin).translateY(size));
return root;
},
drawLeaf: function(stem) {
stem.skin.color.setRGB(0, 1, 0);
stem.add(new THREE.Mesh(new THREE.CylinderGeometry(0, 0.02, 0.6),
stem.skin) .rotateX(0.3).translateY(0.3));
stem.add(new THREE.Mesh(new THREE.CircleGeometry(0.2),
stem.skin) .rotateX(0.3).translateY(0.4));
return stem;
},
Thing: function() {
THREE.Object3D.call(this);
this.skin = new THREE.MeshLambertMaterial({
color: new THREE.Color(0.5, 0.3, 0.2),
vertexColors: THREE.FaceColors,
side: THREE.DoubleSide
})
}
};
pen3D.Thing.prototype = Object.create(THREE.Object3D.prototype);
pen3D.Thing.prototype.tilt = pen3D.Thing.prototype.rotateX;
pen3D.Thing.prototype.turn = pen3D.Thing.prototype.rotateY;
pen3D.Thing.prototype.move = pen3D.Thing.prototype.translateY;
pen3D.center = new pen3D.Thing;
scene.add(pen3D.center);
return pen3D;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
Le contenu 3D est caché dans setup3D () et n’est inclus que pour le rendre moins ennuyeux que console.log (). Les anges sont mesurés en radians par la voie.
Testé pour fonctionner dans Firefox et Chrome. Non implémenté dans Internet Explore et iOS (iPads). Essayez de le faire vous-même.
Après un autre passage des réponses, j’ai trouvé que Gabriel Ratener avait fait une réponse similaire l’an dernier: https://stackoverflow.com/a/24401317/5032384
Le code tiré de ceci lien ne gèlera pas comp. Mais cela ne fonctionne que sur ff.
/**
* Netscape compatible WaitForDelay function.
* You can use it as an alternative to Thread.Sleep() in any major programming language
* that support it while JavaScript it self doesn't have any built-in function to do such a thing.
* parameters:
* (Number) delay in millisecond
*/
function nsWaitForDelay(delay) {
/**
* Just uncomment this code if you're building an extention for Firefox.
* Since FF3, we'll have to ask for user permission to execute XPCOM objects.
*/
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
// Get the current thread.
var thread = Components.classes["@mozilla.org/thread-manager;1"].getService(Components.interfaces.nsIThreadManager).currentThread;
// Create an inner property to be used later as a notifier.
this.delayed = true;
/* Call JavaScript setTimeout function
* to execute this.delayed = false
* after it finish.
*/
setTimeout("this.delayed = false;", delay);
/**
* Keep looping until this.delayed = false
*/
while (this.delayed) {
/**
* This code will not freeze your browser as it's documented in here:
* https://developer.mozilla.org/en/Code_snippets/Threads#Waiting_for_a_background_task_to_complete
*/
thread.processNextEvent(true);
}
}
Depuis le noeud 7.6, vous pouvez combiner la fonction promisify
du module utils avec setTimeout
.
const sleep = require('util').promisify(setTimeout)
async function main() {
console.time("Slept for")
await sleep(3000)
console.timeEnd("Slept for")
}
main()
async function asyncGenerator() {
while (goOn) {
var fileList = await listFiles(nextPageToken);
await sleep(3000)
var parents = await requestParents(fileList);
}
}
Si vous avez raison, une fonction de sommeil comme celle-ci
var sleep = function(period, decision, callback){
var interval = setInterval(function(){
if (decision()) {
interval = clearInterval(interval);
callback();
}
}, period);
}
et vous avez une fonction asynchrone pour appeler plusieurs fois
var xhr = function(url, callback){
// make ajax request
// call callback when request fulfills
}
Et vous configurez votre projet comme ceci:
var ready = false;
function xhr1(){
xhr(url1, function(){ ready = true;});
}
function xhr2(){
xhr(url2, function(){ ready = true; });
}
function xhr3(){
xhr(url3, function(){ ready = true; });
}
Ensuite, vous pouvez faire ceci:
xhr1();
sleep(100, function(){ return done; }, xhr2);
sleep(100, function(){ return done; }, xhr3);
sleep(100, function(){ return done; }, function(){
// do more
});
Au lieu d'une indentation de rappel sans fin comme celle-ci:
xhr(url1, function(){
xhr2(url2, function(){
xhr3(url3, function(){
// do more
});
});
});
Tout d’abord - setTimeout et setInterval sont ce que devrait être utilisé, en raison de la nature rappelable de javascript. Si vous souhaitez utiliser sleep()
, le flux de contrôle ou l'architecture de votre code est incorrect.
Cela dit, je suppose que je peux toujours aider avec deux mises en œuvre d’un sommeil.
simulant une course synchrone au sommet de ma tête:
//a module to do taht //dual-license: MIT or WTF [you can use it anyhow and leave my nickname in a comment if you want to]
var _=(function(){
var queue=[];
var play=function(){
var go=queue.shift();
if(go){if(go.a){go.f();play();}else{setTimeout(play,go.t);}}
}
return {
go:function(f){
queue.Push({a:1,f:f});
},
sleep:function(t){
queue.Push({a:0,t:t});
},
playback:play
}
})();
[rendre la lecture automatique devrait également être possible]
//usage
_.go(function(){
//your code
console.log('first');
});
_.sleep(5000);
_.go(function(){
//your code
console.log('next');
});
//this triggers the simulation
_.playback();
véritable course synchrone
J'ai beaucoup réfléchi un jour et la seule idée que je puisse avoir pour un vrai sommeil en javascript est technique.
une fonction de veille devrait être un appel synchrone AJAX avec un délai d'attente défini sur la valeur de veille. C'est tout et un seul moyen d'avoir une vraie sleep()
Au cas où vous auriez vraiment besoin d’un sommeil (), rien que pour tester quelque chose Mais sachez que cela bloquera le navigateur la plupart du temps lors du débogage - c'est probablement pourquoi vous en avez besoin de toute façon. En mode production, je commenterai cette fonction.
function pauseBrowser(millis) {
var date = Date.now();
var curDate = null;
do {
curDate = Date.now();
} while (curDate-date < millis);
}
N'utilisez pas new Date () dans la boucle, sauf si vous souhaitez perdre de la mémoire, de la puissance de traitement, de la batterie et éventuellement la durée de vie de votre appareil.
Une fonction à mettre en veille, utilisant un appel synchrone pour laisser le système d'exploitation le faire. Utilisez n'importe quelle commande de veille du système d'exploitation que vous aimez. Il n’est pas occupé à attendre en termes d’utilisation du temps processeur.
J'ai choisi le ping sur une adresse inexistante.
const cp = require('child_process');
function sleep(ms)
{
try{cp.execSync('ping 192.0.2.0 -n 1 -w '+ms);}
catch(err){}
}
Un test pour vérifier que ça marche
console.log(Date.now());
console.log(Date.now());
sleep(10000);
console.log(Date.now());
console.log(Date.now());
Et quelques résultats de test.
1491575275136
1491575275157
(et après 10 secondes)
1491575285075
1491575285076
Ce n'est vraiment pas une bonne idée, faire quelque chose comme ceci causerait le blocage de la page entière pendant que le système attendait le retour de votre fonction.
Si vous aimez un conseil de ne pas perdre de performance. setTimeout
est votre prévu sleep
. Cependant, si vous voulez une syntaxe où le code est "divisé au milieu" par sleep
, nous pouvons faire:
sleep=function(tm,fn){
window.setTimeout(fn,tm);
}
alors, perpare fonctionne comme suit:
var fnBeforeSleep=function(){
//All codes before sleep
}
var fnAfterSleep=function(){
//All codes after sleep
}
Ensuite :
fnBeforeSleep();
sleep(2000,
fnAfterSleep);
fnBeforeSleep();
sleep(2000);
fnAfterSleep();
Mise à jour 2019 avec Atomics.wait
Devrait fonctionner dans Node 9.3 ou supérieur.
J'avais besoin d'une minuterie assez précise dans Node.js et cela fonctionne très bien pour cela. Cependant, il semble que le support des navigateurs soit extrêmement limité.
let ms = 10000;
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
A couru quelques repères de minuterie de 10 secondes.
Avec setTimeout, j'obtiens une erreur pouvant aller jusqu'à 7 000 microsecondes. (7ms)
Avec Atomics, mon erreur semble rester inférieure à 600 microsecondes. (0.6ms)
Avec await
support et Bluebird promise :
await bluebird.delay(1000);
Cela fonctionnera comme un sleep(1)
synchrone du langage c. Ma solution préférée.
await new Promise(resolve => setTimeout(resolve, 2000));
assurez-vous que votre fonction d'appel est asynchrone
vérifié et fonctionne bien
Embrassez la nature asynchrone de javascript!
Tous les éléments suivants sont renvoyés immédiatement, mais vous n'avez qu'un seul endroit pour insérer le code que vous souhaitez exécuter après un événement.
Les méthodes que j'ai décrites ici sont toutes destinées à différents cas d'utilisation et sont ordonnées en termes de complexité.
Les différentes choses sont les suivantes:
Wait
Attendre de savoir si une condition est vraie est utilisé lorsqu'il n'y a pas de rappel accessible pour vous dire quand quelque chose a fini de s'exécuter.
Ceci est une jolie implémentation de base qui suppose que la condition deviendra vraie à un moment donné. Avec quelques ajustements, il pourrait être étendu pour être encore plus utile (par exemple en fixant une limite d'appels). (J'ai seulement écrit celui-ci hier!)
function waitFor(predicate, successCallback) {
setTimeout(function () {
var result = predicate();
if (result !== undefined)
successCallback(result);
else
waitFor(predicate, successCallback);
}, 100);
}
code d'appel:
beforeEach(function (done) {
selectListField('A field');
waitFor(function () {
var availableOptions = stores.scrapeStore(optionStore);
if (availableOptions.length !== 0)
return availableOptions;
}, done);
});
Ici, j'appelle quelque chose qui charge un 'magasin' externe et attend jusqu'à ce que le magasin contienne quelque chose avant de continuer (beforeEach est un framework de test jasmine).
Attendez que plusieurs choses soient terminées
Une autre chose que je devais faire était d'exécuter un seul rappel après le chargement de différentes méthodes. Vous pouvez faire ça comme ça:
createWaitRunner = function (completionCallback) {
var callback = completionCallback;
var completionRecord = [];
var elements = 0;
function maybeFinish() {
var done = completionRecord.every(function (element) {
return element === true
});
if (done)
callback();
}
return {
getNotifier: function (func) {
func = func || function (){};
var index = elements++;
completionRecord[index] = false;
return function () {
func.applyTo(arguments);
completionRecord[index] = true;
maybeFinish();
}
}
}
};
code d'appel:
var waiter = createWaitRunner(done);
filterList.bindStore = waiter.getNotifier();
includeGrid.reconfigure = waiter.getNotifier(function (store) {
includeStore = store;
});
excludeGrid.reconfigure = waiter.getNotifier(function (store) {
excludeStore = store;
});
Soit vous attendez simplement les notifications, soit vous pouvez également envelopper d’autres fonctions qui utilisent les valeurs transmises à la fonction. Lorsque toutes les méthodes sont appelées, done
sera exécuté.
Exécution de méthodes asynchrones dans l'ordre
J'ai utilisé une approche différente lorsque j'avais une série de méthodes asynchrones à appeler consécutivement (encore une fois lors des tests). Cela ressemble un peu à quelque chose dans lequel vous pouvez entrer la bibliothèque Async - la série fait à peu près la même chose et j'ai tout d'abord lu un peu cette bibliothèque pour voir si elle répondait à mes attentes. Je pense cependant que la mienne a une meilleure API pour travailler avec des tests (+ c’était amusant à mettre en œuvre!).
//provides a context for running asyncronous methods syncronously
//the context just provides a way of sharing bits of state
//use run to execute the methods. These should be methods that take a callback and optionally the context as arguments
//note the callback is provided first so you have the option of just partially applying your function to the arguments you want
//instead of having to wrap even simple functions in another function
//when adding steps you can supply either just a function or a variable name and a function
//if you supply a variable name then the output of the function (which should be passed into the callback) will be written to the context
createSynchronisedRunner = function (doneFunction) {
var context = {};
var currentPosition = 0;
var steps = [];
//this is the loop. it is triggered again when each method finishes
var runNext = function () {
var step = steps[currentPosition];
step.func.call(null,
function (output) {
step.outputHandler(output);
currentPosition++;
if (currentPosition === steps.length)
return;
runNext();
}, context);
};
var api = {};
api.addStep = function (firstArg, secondArg) {
var assignOutput;
var func;
//overloads
if (secondArg === undefined) {
assignOutput = function () {
};
func = firstArg;
}
else {
var propertyName = firstArg;
assignOutput = function (output) {
context[propertyName] = output;
};
func = secondArg;
}
steps.Push({
func: func,
outputHandler: assignOutput
});
};
api.run = function (completedAllCallback) {
completedAllCallback = completedAllCallback || function(){};
var lastStep = steps[steps.length - 1];
var currentHandler = lastStep.outputHandler;
lastStep.outputHandler = function (output) {
currentHandler(output);
completedAllCallback(context);
doneFunction();
};
runNext();
};
//this is to support more flexible use where you use a done function in a different scope to initialisation
//eg the done of a test but create in a beforeEach
api.setDoneCallback = function (done) {
doneFunction = done;
};
return api;
};
code d'appel:
beforeAll(function (done) {
var runner = createSynchronisedRunner(done);
runner.addStep('attachmentInformation', testEventService.getAttachmentCalled.partiallyApplyTo('cat eating lots of memory.jpg'));
runner.addStep('attachment', getAttachment.partiallyApplyTo("cat eating lots of memory.jpg"));
runner.addStep('noAttachment', getAttachment.partiallyApplyTo("somethingElse.jpg"));
runner.run(function (context) {
attachment = context.attachment;
noAttachment = context.noAttachment;
});
});
PartiallyApplyTo est une version renommée de la mise en oeuvre de Doug Crockford de Curry. Une grande partie des éléments sur lesquels je travaille prennent un rappel comme argument final afin que des appels simples puissent être effectués comme ceci plutôt que de devoir tout emballer avec une fonction supplémentaire.
Espérons que certaines des idées proposées pourraient être utiles aux gens.
Je sais que la question concerne le sommeil, et la réponse est clairement que ce n'est pas possible. Je pense qu’un désir commun de dormir est de gérer les tâches asynchrones dans l’ordre, je sais que j’ai dû y faire face avec certitude.
De nombreux cas peuvent être en mesure d'utiliser des promesses (utilisation courante de demandes AJAX). Ils vous permettent de faire des choses asynchrones de manière synchrone. Il y a aussi la gestion du succès/de l'échec, et ils peuvent être enchaînés.
Ils font partie de ECMAScript 6; le support du navigateur n’y est donc pas encore complet, principalement, IE ne les prend pas en charge. Il existe également une bibliothèque appelée Q pour faire des promesses.
Références: http://www.html5rocks.com/en/tutorials/es6/promises/
https://github.com/jakearchibald/es6-promise#readme (Shim pour les navigateurs plus anciens ou IE)
Cela vous fera l'affaire.
var reloadAfter = 10; //seconds
var intervalId = setTimeout(function() {
//code you want to execute after the time waiting
}, reloadAfter * 1000); // 60000 = 60 sec = 1 min
Essayez cette fonction javascript simple:
function sleep(milliseconds) {
var $return = false;
if (typeof importScripts == 'function') {
var sleep_xhr = function (milliseconds) {
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://128.0.0.1:' + (Math.random() * 100000).toFixed(0) + '/', false);
xhr.timeout = milliseconds;
xhr.send();
} catch (E) {
// Nothing to do...
}
};
milliseconds = milliseconds | 0;
if (milliseconds > 0) {
var start = Date.now();
while (Date.now() < start + milliseconds) {
sleep_xhr((start + milliseconds) - Date.now());
}
$return = Date.now() - start;
}
}
return $return;
}
Remarque: cette fonction ne fonctionne que sur travailleurs Web.
Vous pouvez utiliser un appel de clôture setTimeout () avec des valeurs incrémentielles plus grandes.
var items = ['item1', 'item2', 'item3'];
function functionToExecute(item) {
console.log('function executed for item: ' + item);
}
$.each(items, function (index, item) {
var timeoutValue = index * 2000;
setTimeout(function() {
console.log('waited ' + timeoutValue + ' milliseconds');
functionToExecute(item);
}, timeoutValue);
});
Résultat:
waited 0 milliseconds
function executed for item: item1
waited 2000 milliseconds
function executed for item: item2
waited 4000 milliseconds
function executed for item: item3
Si vous souhaitez mettre en sommeil une fonction anonyme comme celle que vous avez créée en tant que gestionnaire, je vous recommande ce qui suit:
function()
{
if (!wait_condition)
{
setTimeout(arguments.callee, 100, /*comma-separated arguments here*/);
}
//rest of function
}
Ce code indique "Si la condition d'attente n'a pas encore été remplie, appelez à nouveau cette fonction avec ces arguments". J'ai utilisé cette méthode pour transmettre les mêmes arguments à mes gestionnaires, ce qui en fait un code sleep () sans interrogation (qui ne fonctionne qu'au début de votre fonction).
Le problème avec la plupart des solutions ici est qu'elles rembobinent la pile. Cela peut être un gros problème dans certains cas. Dans cet exemple, je montre comment utiliser les itérateurs de différentes manières pour simuler sommeil réel =
Dans cet exemple, le générateur appelle sa propre next()
alors, une fois lancé, il le fait tout seul.
var h=a();
h.next().value.r=h; //that's how U run it, best I came up with
//sleep without breaking stack !!!
function *a(){
var obj= {};
console.log("going to sleep....2s")
setTimeout(function(){obj.r.next();},2000)
yield obj;
console.log("woke up");
console.log("going to sleep no 2....2s")
setTimeout(function(){obj.r.next();},2000)
yield obj;
console.log("woke up");
console.log("going to sleep no 3....2s")
setTimeout(function(){obj.r.next();},2000)
yield obj;
console.log("done");
}
Il existe une nouvelle bibliothèque qui enchaîne les fonctions avec des délais afin que vous puissiez éviter les rappels.
Tourne ceci:
setTimeout(function(timeout){
function1();
setTimeout(function(timeout){
function2();
setTimeout(function(timeout){
function3();
}, timeout, timeout)
}, timeout, timeout)
}, 10, 10);
dans ceci:
Sequencr.chain([function1, function2, function3], 10);
Et a un support intégré pour les boucles qui "dorment" entre chaque itération.
Une méthode d'un objet qui doit utiliser une méthode "sleep" telle que:
function SomeObject() {
this.SomeProperty = "xxx";
return this;
}
SomeObject.prototype.SomeMethod = function () {
this.DoSomething1(arg1);
sleep(500);
this.DoSomething2(arg1);
}
Peut presque être traduit en:
function SomeObject() {
this.SomeProperty = "xxx";
return this;
}
SomeObject.prototype.SomeMethod = function (arg1) {
var self = this;
self.DoSomething1(arg1);
setTimeout(function () {
self.DoSomething2(arg1);
}, 500);
}
La différence est que l'opération de "SomeMethod" est renvoyée avant que l'opération "DoSomething2" ne soit exécutée. L'appelant de "SomeMethod" ne peut pas en dépendre. Comme la méthode "Sleep" n'existe pas, j'utilise la méthode la plus récente et conçoit mon code en conséquence.
J'espère que ça aide.
Pour résumer (comme cela a été dit dans les réponses précédentes):
Il n'y a pas de fonction de sommeil intégrée en JavaScript. Vous devez utiliser setTimeout ou setInterval pour obtenir un effet similaire.
Si vous le souhaitiez vraiment, vous pouvez simuler une fonctionnalité de veille avec une boucle for telle que celle présentée dans la question d'origine, mais cela ferait fonctionner votre processeur à la perfection. Dans un Web Worker, une solution alternative consisterait à créer un XMLHttpRequest
synchrone sur une adresse IP non sensible et à définir un délai d'expiration approprié. Cela éviterait le problème d'utilisation du processeur. Voici un exemple de code:
// Works only inside a web worker
function sleep(milliseconds) {
var req = new XMLHttpRequest();
req.open("GET", "http://192.0.2.0/", false);
req.timeout = milliseconds;
try {
req.send();
} catch (ex) {
}
}
console.log('Sleeping for 1 second...');
sleep(1000);
console.log('Slept!');
console.log('Sleeping for 5 seconds...')
sleep(5000);
console.log('Slept!');
Je navigue dans la solution pendant un jour mais je réfléchis toujours à la façon de maintenir la possibilité de chaînage lors de l’utilisation de callback. Tout le monde connaît le style de programmation traditionnel qui consiste à exécuter le code ligne par ligne de manière synchronisée. SetTimeout utilise un rappel pour que la ligne suivante n'attende pas qu'elle se termine. Cela me laisse penser à la façon de la "synchroniser", afin de créer une fonction "veille".
Commençant par une simple coroutine:
function coroutine() {
console.log('coroutine-1:start');
sleepFor(3000); //sleep for 3 seconds here
console.log('coroutine-2:complete');
}
Je veux dormir 3 secondes au milieu mais je ne veux pas dominer tout le flux, donc la coroutine doit être exécutée par un autre thread. Je considère le nity YieldInstruction , et modifie la coroutine comme suit:
function coroutine1() {
this.a = 100;
console.log('coroutine1-1:start');
return sleepFor(3000).yield; // sleep for 3 seconds here
console.log('coroutine1-2:complete');
this.a++;
}
var c1 = new coroutine1();
Déclarez le prototype sleepFor:
sleepFor = function(ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
setTimeout(function() {
new Function(funcArgs, funcBody).apply(context, args);
}, ms);
return this;
}
Après avoir exécuté la coroutine1 (j'ai testé dans IE11 et Chrome49), vous verrez qu'il dort 3 secondes entre deux instructions de la console. Il garde les codes aussi jolis que le style traditionnel. Le problème est en sommeil pour la routine. Il lit le corps de la fonction appelant sous forme de chaîne et le divise en 2 parties. Retirez la partie supérieure et créez une autre fonction par la partie inférieure. Après avoir attendu le nombre de millisecondes spécifié, il appelle la fonction créée en appliquant le contexte et les arguments d'origine. Pour le flux d'origine, il se terminera par "return" comme d'habitude. Pour le "rendement"? Il est utilisé pour l'appariement de regex. C'est nécessaire mais pas du tout utile.
Ce n'est pas du tout parfait à 100% mais cela me permet au moins d'atteindre mon travail. Je dois mentionner certaines limites à l’utilisation de ce code. Comme le code est divisé en deux parties, l’instruction "return" doit être en externe, au lieu de toute boucle ou {}. c'est à dire.
function coroutine3() {
this.a = 100;
console.log('coroutine3-1:start');
if(true) {
return sleepFor(3000).yield;
} // <- raise exception here
console.log('coroutine3-2:complete');
this.a++;
}
Les codes ci-dessus doivent poser problème car le crochet de fermeture ne peut exister individuellement dans la fonction créée. Une autre limitation est que toutes les variables locales déclarées par "var xxx = 123" ne peuvent pas être transférées à la fonction suivante. Vous devez utiliser "this.xxx = 123" pour obtenir la même chose. Si votre fonction a des arguments et qu'ils ont des modifications, la valeur modifiée ne peut pas non plus être transmise à la fonction suivante.
function coroutine4(x) { // assume x=abc
var z = x;
x = 'def';
console.log('coroutine4-1:start' + z + x); //z=abc, x=def
return sleepFor(3000).yield;
console.log('coroutine4-2:' + z + x); //z=undefined, x=abc
}
Je voudrais introduire un autre prototype de fonction: waitFor
waitFor = function(check, ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
var thread = setInterval(function() {
if(check()) {
clearInterval(thread);
new Function(funcArgs, funcBody).apply(context, args);
}
}, ms?ms:100);
return this;
}
Il attend la fonction "check" jusqu'à ce qu'il retourne à true. Il vérifie la valeur toutes les 100ms. Vous pouvez l'ajuster en passant un argument supplémentaire. Considérons le coroutine2 de test:
function coroutine2(c) {
/* some codes here */
this.a = 1;
console.log('coroutine2-1:' + this.a++);
return sleepFor(500).yield;
/* next */
console.log('coroutine2-2:' + this.a++);
console.log('coroutine2-2:waitFor c.a>100:' + c.a);
return waitFor(function() {
return c.a>100;
}).yield;
/* the rest of code */
console.log('coroutine2-3:' + this.a++);
}
Aussi dans le style que nous aimons jusqu'à présent. En fait, je déteste le rappel imbriqué. Il est facile de comprendre que la coroutine2 attendra l’achèvement de la coroutine1. Intéressant? Ok, lancez les codes suivants:
this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);
La sortie est:
outer-1:10
coroutine1-1:start
coroutine2-1:1
outer-2:11
coroutine2-2:2
coroutine2-2:waitFor c.a>100:100
coroutine1-2:complete
coroutine2-3:3
La partie externe est immédiatement terminée après la coroutine1 et la coroutine2 initialisées. Ensuite, coroutine1 attendra 3000ms. Coroutine2 entrera dans l'étape 2 après avoir attendu 500 ms. Après cela, il passera à l'étape 3 une fois qu'il aura détecté les valeurs de coroutine1.a> 100.
Attention, il y a 3 contextes pour contenir la variable "a". L'un est externe, les valeurs 10 et 11. Un autre est coroutine1, les valeurs 100 et 101. Le dernier est coroutine2, les valeurs 1,2 et 3. En coroutine2, il attend aussi ca qui vient. depuis coroutine1, jusqu'à ce que sa valeur soit supérieure à 100. 3 contextes sont indépendants.
Le code complet pour copier/coller:
sleepFor = function(ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
setTimeout(function() {
new Function(funcArgs, funcBody).apply(context, args);
}, ms);
return this;
}
waitFor = function(check, ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
var thread = setInterval(function() {
if(check()) {
clearInterval(thread);
new Function(funcArgs, funcBody).apply(context, args);
}
}, ms?ms:100);
return this;
}
function coroutine1() {
this.a = 100;
console.log('coroutine1-1:start');
return sleepFor(3000).yield;
console.log('coroutine1-2:complete');
this.a++;
}
function coroutine2(c) {
/* some codes here */
this.a = 1;
console.log('coroutine2-1:' + this.a++);
return sleepFor(500).yield;
/* next */
console.log('coroutine2-2:' + this.a++);
console.log('coroutine2-2:waitFor c.a>100:' + c.a);
return waitFor(function() {
return c.a>100;
}).yield;
/* the rest of code */
console.log('coroutine2-3:' + this.a++);
}
this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);
Il est testé dans IE11 et Chrome49. Parce qu'il utilise arguments.callee, il peut donc être gênant s'il s'exécute en mode strict.
Sur le serveur, vous pouvez utiliser la méthode deasyncsleep()
, implémentée de manière native dans C so il peut effectivement implémenter un effet wait sans bloquer la boucle d'événement ou mettre votre processeur à 100% de la charge.
Exemple:
#!/usr/bin/env node
// Requires `npm install --save deasync`
var sleep = require("deasync").sleep;
sleep(5000);
console.log ("Hello World!!");
Mais, si vous avez besoin d’une fonction javascript pure (par exemple, pour l’exécuter côté client par un navigateur), je suis désolée de dire que Je pense que votre fonction pausecomp()
est la seule façon de l'aborder et, plus que cela:
Cela met en pause non seulement votre fonction mais toute la boucle d’événement. Donc, aucun autre événement ne sera assisté.
Il met votre processeur à 100% de charge.
Donc, si vous en avez besoin pour un script de navigateur et que vous ne voulez pas ces terribles effets, je dois dire que vous devriez repenser votre fonction de la manière suivante:
une). Vous pouvez le rappeler (ou appeler une fonction do_the_rest()
) à un moment donné. Le moyen le plus simple si vous n'attendez aucun résultat de votre fonction.
b) Ou, si vous devez attendre un résultat, vous devez alors utiliser des promesses (ou un rappel, bien sûr ;-)).
Aucun résultat attendu exemple:
function myFunc() {
console.log ("Do some things");
setTimeout(function doTheRest(){
console.log ("Do more things...");
}, 5000);
// Returns undefined.
};
myFunc();
Exemple de retour d'une promesse (notez que cela modifie l'utilisation de votre fonction):
function myFunc(someString) {
return new Promise(function(resolve, reject) {
var result = [someString];
result.Push("Do some things");
setTimeout(function(){
result.Push("Do more things...");
resolve(result.join("\n"));
}, 5000);
});
};
// But notice that this approach affect to the function usage...
// (It returns a promise, not actual data):
myFunc("Hello!!").then(function(data){
console.log(data);
}).catch(function(err){
console.error(err);
});
J'ai eu un problème similaire: attendre l'existence du contrôle et vérifier les intervalles. Puisqu'il n'y a pas vraiment de sommeil, attendre ou faire une pause dans JavaScript et que l'attente/async n'est pas supportée correctement dans Internet Explorer, j'ai conçu une solution en utilisant setTimeOut et en injectant la fonction en cas de recherche réussie de l'élément. Voici l'exemple de code complet, afin que tout le monde puisse le reproduire et l'utiliser pour son propre projet:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
var ElementSearchStatus = {
None: 0,
Found: 1,
NotFound: 2,
Timeout: 3
};
var maxTimeout = 5;
var timeoutMiliseconds = 1000;
function waitForElement(elementId, count, timeout, onSuccessFunction) {
++count;
var elementSearchStatus = existsElement(elementId, count, timeout);
if (elementSearchStatus == ElementSearchStatus.None) {
window.setTimeout(waitForElement, timeoutMiliseconds, elementId, count, timeout, onSuccessFunction);
}
else {
if (elementSearchStatus == ElementSearchStatus.Found) {
onSuccessFunction();
}
}
}
function existsElement(elementId, count, timeout) {
var foundElements = $("#" + elementId);
if (foundElements.length > 0 || count > timeout) {
if (foundElements.length > 0) {
console.log(elementId + " found");
return ElementSearchStatus.Found;
}
else {
console.log("Search for " + elementId + " timed out after " + count + " tries.");
return ElementSearchStatus.Timeout;
}
}
else {
console.log("waiting for " + elementId + " after " + count + " of " + timeout);
return ElementSearchStatus.None;
}
}
function main() {
waitForElement("StartButton", 0, maxTimeout, function () {
console.log("found StartButton!");
DoOtherStuff("StartButton2")
});
}
function DoOtherStuff(elementId) {
waitForElement(elementId, 0, maxTimeout, function () {
console.log("found " + elementId);
DoOtherStuff("StartButton3");
});
}
</script>
</head>
<body>
<button type="button" id="StartButton" onclick="main();">Start Test</button>
<button type="button" id="StartButton2" onclick="alert('Hey ya Start Button 2');">Show alert</button>
</body>
</html>
Si vous vraiment voulez mettre en pause un script, vous pouvez faire ceci:
var milliseconds;
var pretime;
var stage;
function step(time){
switch(stage){
case 0:
//Code before the pause
pretime=time;
milliseconds=XXX;
stage=1;
break;
case 1:
//Code that is looped through while paused
if(time-pretime >= milliseconds){
//Code after the pause
pretime=time;
milliseconds=XXX;
stage=2;
}
break;
case 2:
//Code that is looped through while paused
if(time-pretime >= milliseconds){
//Code after the pause
pretime=time;
milliseconds=XXX;
stage=3;
}
break;
case 3:
//Etc...
}
Window.requestAnimationFrame(step)
}
step();
C’est probablement exactement ce que vous voulez si vous utilisez quand même une boucle, et vous pouvez la changer de manière à avoir un pseudo-multi-threading, où certaines fonctions sont en attente et d'autres qui fonctionnent normalement. Je l'utilise tout le temps pour les jeux de pure JS.
J'ai cette question depuis longtemps et la réponse dont j'avais besoin n'était pas exactement celle qui a été fournie ici. Cette fonction d'attente provoque une attente synchrone qui ne bloque pas le processeur. waitForIt fait une demande ajax à n'importe où et définit l'indicateur asynchrone sur false. waitF fait de même avec un cadre et waitD fait de même avec un div. Ajax prend environ 100 ms, le cadre est environ 25, et div est environ 1. La fonction d'attente exploite tous ces facteurs en fonction du temps que vous lui donnez. S'il n'a pas attendu assez longtemps, recommencez. J'ai besoin de cela lorsqu'il s'agit de plusieurs éléments de chargement asynchrones. Fondamentalement pour 'attendre jusqu'à ce que cet élément existe'. Vous pouvez y jouer ici https://jsfiddle.net/h2vm29ue/ Il exploite simplement les éléments que le navigateur attend naturellement. La version plus longue https://jsfiddle.net/5cov1p0z/32/ est plus précise.
function waitForIt() {
var start = new Date();
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//doesn't matter
}
};
xhttp.open("GET", "WaitForIt", false);
xhttp.send();
var end = new Date();
}
//
function waitF() {
var start = new Date();
var ifram = document.createElement('iframe');
ifram.id = 'ifram';
ifram.src = '';
var div = document.createElement('div');
div.id = 'timer';
document.body.appendChild(div);
document.getElementById('timer').appendChild(ifram);
document.getElementById('timer').removeChild(ifram);
document.body.removeChild(div);
var end = new Date();
return (end - start);
}
function waitD() {
var start = new Date();
var div = document.createElement('div');
div.id = 'timer';
document.body.appendChild(div);
div.click();
document.body.removeChild(div);
var end = new Date();
return (end - start);
}
function wait(time) {
var start = new Date();
var end = new Date();
while ((end - start < time)) {
if ((time - (end - start)) >= 200) {
waitForIt();
} else {
if ((time - (end - start)) >= 50) {
waitF();
} else {
waitD();
}
}
end = new Date();
}
return (end - start);
}
Promise n'est pas un constructeur utilisant la réponse principale. Si vous importez bluebird, vous pouvez le faire. La solution la plus simple imo.
import * as Promise from 'bluebird';
await Promise.delay(5000)
Un moyen très simple de dormir, qui SERA compatible avec tout ce qui fonctionne avec Javascript ... Ce code a été testé avec environ 500 entrées, l'utilisation du processeur et de la mémoire toujours non visible sur mes navigateurs Web.
Voici une fonction qui attend que le nœud soit visible ...
Cette fonction crée un nouveau contexte function () {}
pour éviter la récursivité. Nous avons placé un code qui fait la même chose que le code de l'appelant dans ce nouveau contexte. Nous utilisons la fonction Timeout
pour appeler notre fonction après quelques secondes.
var get_hyper = function (node , maxcount , only_relation) {
if (node.offsetParent === null) {
// node is hidden
setTimeout(function () { get_hyper(node , maxcount , only_relation)}
,1000);
return;
};
// Enter here the code that wait that that the node is visible
// before getting executed.
};
La réponse courte est NO, pas en javascript. Votre solution semble être le seul moyen de ne pas rendre le contrôle à l'environnement.
Cela est nécessaire si l'environnement ne prend pas en charge les événements. Ils ne soutiendraient probablement pas le settimeout non plus.
settimeout est sans aucun doute la meilleure solution si vous êtes dans un environnement événementiel tel qu'un navigateur ou un noeud.js.
Le problème de l’utilisation d’une fonction de veille réelle est que JavaScript est constitué d’un seul thread et qu’une fonction de veille fera en sorte que l’onglet de votre navigateur se bloque pendant cette durée.
Les fonctions Javascript ne permettent aucune suspension. Avec les procédures Javascript synchrones sont implémentées. Les procédures attendent les opérations d'E/S et les délais d'attente. Disponible pour javascript 1.7.
démos: sommeil de démonstrationprocédures pouvant être suspendues
Pour garder le thread principal occupé pendant quelques millisecondes:
function wait(ms) {
const start = performance.now();
while(performance.now() - start < ms);
}
Une bonne solution dans certaines situations consiste à afficher un panneau de messages de niveau supérieur pour arrêter l'interaction de l'utilisateur, puis à le masquer à nouveau lorsque vous obtenez le résultat attendu (de manière asynchrone). Cela permet au navigateur d’accomplir les tâches en arrière-plan, mais met en pause le flux de travail jusqu’à ce que vous obteniez votre résultat.
J'utilise HTML5 Worker multithread qui permettra d'abandonner une XMLHttpRequest synchrone pointant vers une URL qui ne répond pas. Cela ne bloque pas le navigateur.