What is the JavaScript version of sleep()?
在javascript中,有没有比下面的
1 2 3 4 5 6 7 | function pausecomp(millis) { var date = new Date(); var curDate = null; do { curDate = new Date(); } while(curDate-date < millis); } |
这不是javascript中sleep的复制-操作之间的延迟;我希望在函数的中间有一个真正的睡眠,而不是在一段代码执行之前的延迟。
2017更新
自从2009年问到这个问题以来,JavaScript已经有了很大的发展。所有其他答案现在都已过时或过于复杂。以下是当前的最佳实践:
1 2 3 4 5 6 7 8 9 10 11 | 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'); } demo(); |
您可以在runkit上尝试此代码。请注意,
两个新的javascript功能帮助编写了这个实际的"睡眠"功能:
- Promises,ES2015的本土特色(又称ES6)。我们在睡眠功能的定义中也使用了箭头函数。
- 即将推出的
async/await 特性允许代码明确地等待承诺的解决。
兼容性
- 承诺在节点v0.12+中得到支持,在浏览器中得到广泛支持,IE除外。
async /await 降落在V8上,从chrome 55开始默认启用。- 2016年10月登陆节点7
- 并于2016年11月登陆了Firefox Nightly。
如果出于某种原因,您使用的节点早于7,或者针对的是旧的浏览器,那么仍然可以通过babel(一种将javascript+新功能开发为普通的旧javascript的工具)和
1 | npm install babel-cli --save |
创建
1 2 3 4 5 | { "plugins": [ "transform-async-to-generator", ] } |
然后用运行代码
1 | node_modules/babel-cli/bin/babel-node.js sleep.js |
但同样,如果您使用的是节点7或更高版本,或者您的目标是现代浏览器,则不需要这样做。
(见2016年更新答案)
我认为想要执行一个动作,等待,然后再执行另一个动作是完全合理的。如果您习惯于用多线程语言编写,那么您可能会有一个想法,在线程唤醒之前,在一定的时间内生成执行。
这里的问题是javascript是一个基于单线程事件的模型。在特定情况下,让整个引擎等待几秒钟可能会比较好,一般来说这是不好的做法。假设我想在编写自己的函数时使用您的函数?当我调用你的方法时,我的方法都会冻结。如果javascript可以以某种方式保留函数的执行上下文,将其存储在某个地方,然后将其恢复并在稍后继续,那么可能会发生睡眠,但这基本上就是线程化。
因此,您基本上坚持了其他人的建议——您需要将代码分解成多个函数。
那么,你的问题是一个错误的选择。没有办法按照你想要的方式睡觉,你也不应该追求你建议的解决方案。
在javascript中,我重写了每个函数,以便它能够尽快结束。您希望浏览器回到控件中,这样它可以更改您的DOM。
每次我想要在函数的中间休息时,我都重构为使用
I am going to edit this answer because i found this as useful:
在任何一种语言中,臭名昭著的睡眠,或者说是延迟,其功能都备受争议。有些人会说,应该总是有一个信号或回调来触发一个给定的功能,另一些人会说,有时一个任意的延迟时间是有用的。我要说的是,在这个行业中,每个人都有自己的一条规则,不能支配任何东西。
编写sleep函数很简单,而且在javascript承诺下更加有用:
1 2 3 4 5 6 7 8 9 | // sleep time expects milliseconds function sleep (time) { return new Promise((resolve) => setTimeout(resolve, time)); } // Usage! sleep(500).then(() => { // Do something after the sleep! }); |
仅用于调试/dev,如果它对某人有用,我会发布它
有趣的是,在Firebug(可能是其他JS控制台)中,只有在指定的睡眠时间(…)之后,才会在按Enter键后发生任何事情。
1 2 3 4 | function sleepFor( sleepDuration ){ var now = new Date().getTime(); while(new Date().getTime() < now + sleepDuration){ /* do nothing */ } } |
使用实例:
1 | function sleepThenAct(){ sleepFor(2000); console.log("hello js sleep !"); } |
我同意其他的海报,忙碌的睡眠只是个坏主意。
但是,setTimeout不会延迟执行,它会在设置超时后立即执行函数的下一行,而不是在超时到期后执行,这样就不会完成与sleep将完成的任务相同的任务。
这样做的方法是将您的功能分解为前后部分。
1 2 3 4 5 6 7 8 9 10 | function doStuff() { //do some things setTimeout(continueExecution, 10000) //wait ten seconds before continuing } function continueExecution() { //finish doing things after the pause } |
确保您的函数名仍然准确地描述了每一项正在执行的操作(即gathereinputhenwait和checkinput,而不是funcpart1和funcpart2)
编辑
这种方法的目的是在超时之后才执行您决定的代码行,同时仍然将控制权返回到客户机PC以执行它排队等待的任何其他操作。
进一步编辑
正如评论中指出的,这绝对不会在循环中工作。你可以做一些奇特的(丑陋的)黑客行为,使它在一个循环中工作,但一般来说,这只会造成灾难性的意大利面条代码。
为了$DEITY的爱,请不要做忙碌的等待睡眠功能。
我知道这是一个古老的问题,但是如果(像我一样)你在Rhino中使用JavaScript,你可以使用…
1 2 3 4 5 6 7 8 9 10 11 12 | 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. */ } |
如果您使用jquery,实际上有人创建了一个"延迟"插件,它只不过是一个用于设置超时的包装器:
1 2 3 4 5 6 7 8 9 10 11 | // 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; }; |
然后您可以在一行函数调用中按预期使用它:
1 2 3 4 | $('#warning') .addClass('highlight') .delay(1000) .removeClass('highlight'); |
我也搜索过睡眠解决方案(不用于生产代码,仅用于开发/测试),并找到了这篇文章:
http://narayanraman.blogspot.com/2005/12/javascript-sleep-or-wait.html
…下面是另一个与客户端解决方案的链接:http://www.devcasider.com/
另外,当您调用
干得好。正如代码所说,不要做一个坏的开发人员,在网站上使用它。它是一个开发效用函数。
1 2 3 4 5 6 | // 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) {} } |
下面是一个使用同步xmlhttpRequest的简单解决方案:
1 2 3 4 5 | function sleep(n){ var request = new XMLHttpRequest(); request.open('GET', '/sleep.php?n=' + n, false); // `false` makes the request synchronous request.send(null); } |
sleep.php的内容:
1 | <?php sleep($_GET['n']); |
现在,请使用:睡眠(5);
第一:
定义要执行的函数,如下所示:
1 2 3 | function alertWorld(){ alert("Hello World"); } |
然后使用setTimeout方法计划其执行:
1 | setTimeout(alertWorld,1000) |
注意两件事
- 第二个参数是时间(毫秒)
- 作为第一个参数,您必须只传递函数的名称(引用),而不传递括号
我个人喜欢简单的:
1 2 3 4 | function sleep(seconds){ var waitUntil = new Date().getTime() + seconds*1000; while(new Date().getTime() < waitUntil) true; } |
然后:
1 | sleep(2); // Sleeps for 2 seconds |
在p5js中创建脚本时,我一直在使用它来创建假加载时间。
让事情看起来像大多数人想要的更好的解决方案是使用匿名函数:
1 2 3 4 5 6 7 | 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 |
这可能是你最接近做你想做的事情。
注意,如果你需要多个睡眠,这可能会在匆忙中变得难看,你可能实际上需要重新考虑你的设计。
我将把setTimeout封装在与其他异步任务的代码一致性承诺中:fiddle中的demo
1 2 3 4 5 6 | function sleep(ms) { return(new Promise(function(resolve, reject) { setTimeout(function() { resolve(); }, ms); })); } |
使用方法如下:
1 2 3 | sleep(2000).then(function() { // Do something }); |
如果你使用承诺,很容易记住语法。
对于浏览器,我同意setTimeout和setInterval是一种方法。
但是对于服务器端代码,它可能需要一个阻塞函数(例如,这样您就可以有效地进行线程同步)。
如果您使用node.js和meetor,那么可能会遇到在光纤中使用setTimeout的限制。这是服务器端睡眠的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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'); |
请参阅:https://github.com/laverdet/node fibers sleep
这里的大多数答案都是错误的,或者至少是过时的。没有理由让javascript必须是单线程的,事实上并非如此。如今,所有主流浏览器都支持工作人员,而其他的javascript运行时(如rhino和node.js)都支持多线程。
"javascript是单线程的"不是有效答案。例如,在工作线程中运行sleep函数不会阻塞在UI线程中运行的任何代码。
在支持生成器和yield的较新运行时中,可以在单线程环境中为sleep函数带来类似的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // 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); |
这种对睡眠的模仿不同于真正的睡眠功能,因为它不会阻塞线程。它只是Javascript当前setTimeout函数的基础。这种功能类型已经在task.js中实现,现在应该可以在firefox中使用。
1 2 3 4 5 6 7 8 | function sleep(milliseconds) { var start = new Date().getTime(); for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds){ break; } } } |
我在javascript sleep/wait上搜索了很多网页。如果你想让javascript"运行,延迟,运行"…大多数人得到的不是"跑,跑(无用的东西),跑"就是"跑,跑+延迟跑"……
所以我吃了些汉堡然后想:这是一个可行的解决方案…但是你必须把你的运行代码切碎……::是的,我知道,这只是一个更容易阅读的重构…还是…
//………………………………//示例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <html> <body> DISPLAY //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); </body> </html> |
//………………………………//示例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <html> <body> DISPLAY //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 </body> </html> |
//………………例3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <html> <body> DISPLAY //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 </body> </html> |
//第……页例4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <html> <body> DISPLAY //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... </body> </html> |
您可能需要sleep()函数而不是使用setTimeout()的一种情况是,如果您有一个函数响应用户单击,最终将打开一个新的弹出窗口,并且您已经启动了一些处理,需要很短时间才能完成弹出窗口的显示。将打开的窗口移动到关闭状态意味着它通常会被浏览器阻止。
加上我的两位。为了测试的目的,我需要一个忙碌的等待。我不想分割代码,因为这将是一个很大的工作,所以一个简单的为我做。
1 2 3 | for (var i=0;i<1000000;i++){ //waiting } |
我不认为这样做有什么坏处,这对我有好处。
它可以使用Java的睡眠方法来完成。我已经在FF和IE中测试过它,它不会锁定计算机,不会占用资源,也不会导致无休止的服务器点击。对我来说似乎是一个干净的解决方案。
首先,必须让Java加载到页面上并使其方法可用。为此,我做到了:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <html> <head> <script type="text/javascript"> function load() { var appletRef = document.getElementById("app"); window.java = appletRef.Packages.java; } // endfunction <body onloadx="load()"> |
然后,当您想要在JS中无痛暂停时,您所要做的就是:
1 | java.lang.Thread.sleep(xxx) |
其中,xxx是以毫秒为单位的时间。在我的例子中(通过证明),这是一家非常小的公司后端订单履行的一部分,我需要打印一张必须从服务器加载的发票。我是通过将发票(作为网页)加载到iframe中,然后打印iframe来完成的。当然,我必须等到页面完全加载之后才能打印,所以JS必须暂停。我通过让发票页(在iframe中)用onload事件更改父页上的隐藏表单字段来完成这项工作。打印发票的父页上的代码如下(为了清晰起见,不相关的部分被剪切):
1 2 3 4 5 6 7 8 9 10 11 | 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(); |
因此,用户按下按钮,脚本加载发票页面,然后等待,每季度检查一次发票页面是否完成加载,然后弹出打印对话框,供用户将其发送到打印机。QED。
如果必须处理同步执行,我可以理解sleep函数的用途。setInterval和setTimeout函数创建一个并行执行线程,该线程将执行序列返回到主程序,如果必须等待给定的结果,则该线程无效。当然,可以使用事件和处理程序,但在某些情况下,这并不是预期的。
很多答案不能(直接)回答这个问题,这个也不能…
这是我的两分钱(或函数):
如果您想要的函数比
1 2 | function after(ms, fn){ setTimeout(fn, ms); } function every(ms, fn){ setInterval(fn, ms); } |
咖啡说明版本:
1 2 | after = (ms, fn)-> setTimeout fn, ms every = (ms, fn)-> setInterval fn, ms |
然后您可以将它们很好地用于匿名函数:
1 2 3 4 5 6 | after(1000, function(){ console.log("it's been a second"); after(1000, function(){ console.log("it's been another second"); }); }); |
现在,它很容易读取为"n毫秒之后,…"(或"每隔n毫秒,………")
对于希望将循环执行的一组调用隔开的特定情况,您可以在原型中使用类似下面的代码。如果没有原型,可以用setTimeout替换delay函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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) } |
如果您在node.js上,可以查看fibers——节点的本机C扩展,类似于多线程模拟。
它允许您以一种在光纤中阻塞执行的方式执行真正的
以下是他们自述的一个新例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 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'); |
-结果是:
1 2 3 4 | $ 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) |
2009年的老问题。现在,在2015年,一个新的解决方案可能与ECMAScript 2015又名ES6中定义的发电机一起使用。它在6月份得到了批准,但以前在火狐和Chrome中实现过。现在,一个睡眠函数可以在不冻结浏览器的情况下,在循环和子函数中实现非忙、非阻塞和嵌套。只需要纯JavaScript,不需要库或框架。
下面的程序显示了如何制作
使用sleep()或yield的函数必须定义为生成器。在关键字
调用生成器的另一种方法是使用关键字
这一切都由示例
每片叶子都有自己的生命在一个独立的任务中,这个任务从
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 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; } |
1 | <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"> |
3D文件隐藏在setup3d()中,只是为了使它比console.log()更不无聊。顺便说一下,天使是以弧度来衡量的。
在火狐和Chrome上测试过。未在Internet Explore和iOS(iPad)中实现。试着自己跑。
在我找到另一个答案之后,加布里埃尔·拉特纳一年前也给出了类似的答案:https://stackoverflow.com/a/24401317/5032384
在javascript中,您不能这样睡眠,或者更确切地说,您不应该这样睡眠。运行sleep或while循环将导致用户的浏览器挂起,直到循环完成。
使用计时器,如您引用的链接中指定的。
如果你是这样的睡眠功能
1 2 3 4 5 6 7 8 | var sleep = function(period, decision, callback){ var interval = setInterval(function(){ if (decision()) { interval = clearInterval(interval); callback(); } }, period); } |
您有一个异步函数可以多次调用
1 2 3 4 | var xhr = function(url, callback){ // make ajax request // call callback when request fulfills } |
您的项目设置如下:
1 2 3 4 5 6 7 8 9 10 11 | var ready = false; function xhr1(){ xhr(url1, function(){ ready = true;}); } function xhr2(){ xhr(url2, function(){ ready = true; }); } function xhr3(){ xhr(url3, function(){ ready = true; }); } |
然后你可以这样做:
1 2 3 4 5 6 | xhr1(); sleep(100, function(){ return done; }, xhr2); sleep(100, function(){ return done; }, xhr3); sleep(100, function(){ return done; }, function(){ // do more }); |
而不是像这样没完没了的回调缩进:
1 2 3 4 5 6 7 | xhr(url1, function(){ xhr2(url2, function(){ xhr3(url3, function(){ // do more }); }); }); |
从这个链接中获取的代码不会冻结comp。但它只在FF上工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** * 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); } } |
首先,应该使用setTimeout和setInterval,因为javascript具有回调特性。如果您想使用
我已经说过了,我想我仍然可以帮助实现两个睡眠。
假同步跑出我的头顶:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //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 } })(); |
[也可以自动播放]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //usage _.go(function(){ //your code console.log('first'); }); _.sleep(5000); _.go(function(){ //your code console.log('next'); }); //this triggers the simulation _.playback(); |
实同步运行
有一天我仔细考虑了一下,我在javascript中真正睡着的唯一想法是技术上的。
sleep函数必须是一个同步的Ajax调用,并将超时设置为sleep值。只有这样才能拥有真正的EDOCX1[1]
由于节点7.6,您可以将来自utils模块的
1 | const sleep = require('util').promisify(setTimeout) |
一般用法
1 2 3 4 5 6 7 | async function main() { console.time("Slept for") await sleep(3000) console.timeEnd("Slept for") } main() |
问题用法
1 2 3 4 5 6 7 | async function asyncGenerator() { while (goOn) { var fileList = await listFiles(nextPageToken); await sleep(3000) var parents = await requestParents(fileList); } } |
万一你真的需要一个sleep()来测试一些东西。但是请注意,在调试的时候,大多数时候浏览器都会崩溃——这可能就是为什么你无论如何都需要它。在生产模式下,我将对这个函数进行注释。
1 2 3 4 5 6 7 | function pauseBrowser(millis) { var date = Date.now(); var curDate = null; do { curDate = Date.now(); } while (curDate-date < millis); } |
不要在循环中使用new date(),除非您想浪费内存、处理能力、电池以及设备的使用寿命。
有了
这将像C语言的同步
如果你喜欢建议不要失去表现。
1 2 3 | sleep=function(tm,fn){ window.setTimeout(fn,tm); } |
然后,perpare的功能如下:
1 2 3 4 5 6 7 8 9 10 11 | var fnBeforeSleep=function(){ //All codes before sleep } var fnAfterSleep=function(){ //All codes after sleep } |
然后:
1 2 3 | fnBeforeSleep(); sleep(2000, fnAfterSleep); |
是的!?句法上,它非常接近:
1 2 3 | fnBeforeSleep(); sleep(2000); fnAfterSleep(); |
这对你有好处。
1 2 3 4 | var reloadAfter = 10; //seconds var intervalId = setTimeout(function() { //code you want to execute after the time waiting }, reloadAfter * 1000); // 60000 = 60 sec = 1 min |
接受JavaScript的异步特性!
下面的所有内容都将立即返回,但只有一个地方可以放置发生某些事情后要运行的代码。
我在这里概述的方法都适用于不同的用例,并且根据它们的复杂性粗略排序。
不同之处如下:
- 等待某个条件成为现实
- 在调用单个回调之前等待一组方法完成(按任意顺序)
- 在调用回调之前,以特定顺序运行一系列具有共享状态的异步方法
等待
在没有可访问的回调来告诉您某个事件已完成执行时,将使用ful等待查看某个条件是否为真。
这是一个非常基本的实现,它假定条件在某个时刻变为真。通过一些调整,它可以被扩展到更有用的地方(例如通过设置呼叫限制)。(我昨天才写这封信!)
1 2 3 4 5 6 7 8 9 | function waitFor(predicate, successCallback) { setTimeout(function () { var result = predicate(); if (result !== undefined) successCallback(result); else waitFor(predicate, successCallback); }, 100); } |
调用代码:
1 2 3 4 5 6 7 8 9 | beforeEach(function (done) { selectListField('A field'); waitFor(function () { var availableOptions = stores.scrapeStore(optionStore); if (availableOptions.length !== 0) return availableOptions; }, done); }); |
在这里,我调用了加载extjs"store"的函数,并等待该函数包含内容后再继续(beforeeach是Jasmine测试框架的函数)。
等待几件事完成
我需要做的另一件事是在加载完不同的方法之后运行一个回调。你可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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(); } } } }; |
调用代码:
1 2 3 4 5 6 7 8 9 | var waiter = createWaitRunner(done); filterList.bindStore = waiter.getNotifier(); includeGrid.reconfigure = waiter.getNotifier(function (store) { includeStore = store; }); excludeGrid.reconfigure = waiter.getNotifier(function (store) { excludeStore = store; }); |
您可以等待通知,也可以包装其他使用传递给函数的值的函数。调用所有方法后,将运行
按顺序运行异步方法
当我有一系列异步方法在一行中调用时(同样在测试中),我使用了不同的方法。这有点类似于您在异步库中可以得到的东西——series做了同样的事情,我先读了一点该库,看看它是否做了我想要的。我认为我的有一个更好的用于测试的API(实现起来很有趣!).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | //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; }; |
调用代码:
1 2 3 4 5 6 7 8 9 10 | 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; }); }); |
这里的部分应用基本上是道格·克罗克福德的咖喱实现的重命名版本。我正在处理的很多东西都将回调作为最后的参数,这样简单的调用就可以这样完成,而不必用额外的func包装所有东西。
希望里面的一些想法对人们有用。
要休眠的函数,使用同步调用让操作系统执行。使用任何你喜欢的操作系统睡眠命令。在使用CPU时间的意义上,它并不忙于等待。
我选择了一个不存在的地址。
1 2 3 4 5 6 7 | const cp = require('child_process'); function sleep(ms) { try{cp.execSync('ping 192.0.2.0 -n 1 -w '+ms);} catch(err){} } |
验证它工作的测试
1 2 3 4 5 | console.log(Date.now()); console.log(Date.now()); sleep(10000); console.log(Date.now()); console.log(Date.now()); |
以及一些测试结果。
1 2 | 1491575275136 1491575275157 |
(10秒后)
1 2 | 1491575285075 1491575285076 |
这真的不是一个好主意,这样做会导致整个页面在系统等待函数返回时冻结。
我知道问题是关于睡眠,很明显答案是不可能的。我认为睡眠的一个常见需求是有序地处理异步任务,我知道我必须肯定地处理它。
许多情况下可以使用承诺(Ajax请求常用)。它们允许您以同步方式执行异步操作。还有对成功/失败的处理,可以将它们链接起来。
它们是EcmaScript6的一部分,所以浏览器支持还不是全部,主要是IE不支持它们。还有一个图书馆叫Q,是为了履行承诺。
参考文献:http://www.html5rocks.com/en/tutorials/es6/promises/
https://github.com/jakerchibald/es6 promise自述文件(旧版或IE浏览器的填充程序)
尝试这个简单的javascript函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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; } |
注意:此功能仅适用于Web工作者。
如果您希望休眠一个匿名函数,就像您创建的一个处理程序一样,我建议您执行以下操作:
1 2 3 4 5 6 7 8 | function() { if (!wait_condition) { setTimeout(arguments.callee, 100, /*comma-separated arguments here*/); } //rest of function } |
此代码表示"如果等待条件尚未满足,请使用这些参数再次调用此函数。"我使用此方法将相同的参数传递给我的处理程序,有效地使此代码成为非轮询睡眠()(仅在函数开始时有效)。
可以使用具有递增较大值的结束调用setTimeout()。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); }); |
结果:
1 2 3 4 5 6 | waited 0 milliseconds function executed for item: item1 waited 2000 milliseconds function executed for item: item2 waited 4000 milliseconds function executed for item: item3 |
需要使用"睡眠"方法的对象的方法,如:
1 2 3 4 5 6 7 8 9 | function SomeObject() { this.SomeProperty ="xxx"; return this; } SomeObject.prototype.SomeMethod = function () { this.DoSomething1(arg1); sleep(500); this.DoSomething2(arg1); } |
几乎可以翻译成:
1 2 3 4 5 6 7 8 9 10 11 | function SomeObject() { this.SomeProperty ="xxx"; return this; } SomeObject.prototype.SomeMethod = function (arg1) { var self = this; self.DoSomething1(arg1); setTimeout(function () { self.DoSomething2(arg1); }, 500); } |
不同的是,"somemethod"的操作在执行"dosomething2"操作之前返回。"somemethod"的调用方不能依赖于此。由于"sleep"方法不存在,所以我使用后面的方法并相应地设计代码。
我希望这有帮助。
我在这个解决方案中导航了一天,但仍然在思考如何在使用回调时保持可链接性。每个人都熟悉以同步方式逐行运行代码的传统编程风格。setTimeout使用回调,因此下一行不等待它完成。这让我想到如何使它"同步",从而使"睡眠"功能。
从一个简单的连体衣开始:
1 2 3 4 5 | function coroutine() { console.log('coroutine-1:start'); sleepFor(3000); //sleep for 3 seconds here console.log('coroutine-2:complete'); } |
我想在中间睡3秒钟,但不想控制整个流程,所以协程必须由另一个线程执行。我考虑了Unity-yieldinstruction,并在下面修改了coroutine:
1 2 3 4 5 6 7 8 9 | 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(); |
声明Sleepfor原型:
1 2 3 4 5 6 7 8 9 10 11 | 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; } |
在运行coroutine1(我在ie11和chrome49中测试过)之后,您将看到它在两个控制台语句之间休眠3秒。它使代码和传统样式一样漂亮。棘手的事情是在常规的睡眠中。它以字符串形式读取调用函数体,并将其分为两部分。拆下上部,并通过下部创建另一个功能。在等待指定的毫秒数后,它通过应用原始上下文和参数来调用创建的函数。对于原始流,它将像往常一样以"返回"结束。为了"收益"?用于正则表达式匹配。这是必要的,但毫无用处。
它一点也不完美,但至少能完成我的工作。在使用这段代码时,我必须提到一些限制。由于代码被分成两部分,"返回"语句必须在外部,而不是在任何循环或中。即
1 2 3 4 5 6 7 8 9 | 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++; } |
以上代码必须有问题,因为在创建的函数中不能单独存在右括号。另一个限制是由"var xxx=123"声明的所有局部变量都不能带到下一个函数。您必须使用"this.xxx=123"来实现相同的目标。如果您的函数有参数,并且它们得到了更改,那么修改后的值也不能带到下一个函数。
1 2 3 4 5 6 7 | 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 } |
我将介绍另一个函数原型:waitfor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
它等待"check"函数,直到返回true。它每隔100毫秒检查一次值。您可以通过传递附加参数来调整它。考虑测试协作2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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++); } |
同样,我们迄今为止喜爱的漂亮风格。实际上,我讨厌嵌套回调。很容易理解,coroutine2将等待coroutine1完成。有意思吗?好,然后运行以下代码:
1 2 3 4 5 | this.a = 10; console.log('outer-1:' + this.a++); var c1 = new coroutine1(); var c2 = new coroutine2(c1); console.log('outer-2:' + this.a++); |
输出是:
1 2 3 4 5 6 7 8 | 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 |
外部在初始化coroutine1和coroutine2后立即完成。然后,coroutine1等待3000毫秒,等待500毫秒后,coroutine2进入步骤2,检测到coroutine1后,继续步骤3,a值>100。
请注意,有三种上下文可以保存变量"A"。一个是外部的,值为10和11。另一个是coroutine1,值为100和101。最后一个是coroutine2,值为1、2和3。在coroutine2中,它还等待来自coroutine1的c.a,直到其值大于100。3个上下文是独立的。
复制和粘贴的整个代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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++); |
在IE11和Chrome49中测试。因为它使用arguments.callee,所以在严格模式下运行可能会有问题。
有一个新的库,整洁地将函数和超时链接在一起,这样您就可以避免回调地狱。
序列号
变成这样:
1 2 3 4 5 6 7 8 9 | setTimeout(function(timeout){ function1(); setTimeout(function(timeout){ function2(); setTimeout(function(timeout){ function3(); }, timeout, timeout) }, timeout, timeout) }, 10, 10); |
进入这个:
1 | Sequencr.chain([function1, function2, function3], 10); |
并且内置了对在每次迭代之间"休眠"的循环的支持。
这里大多数解决方案的问题是它们倒带堆栈。在某些情况下,这可能是一个大问题。在本例中,我演示了如何以不同的方式使用迭代器来模拟真实的睡眠。
在这个例子中,生成器称它为自己的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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"); } |
如果确实要暂停脚本,可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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(); |
如果无论如何都使用循环,这可能正是您想要的,并且您可以以某种方式对其进行更改,以使您具有伪多线程,其中一些函数等待一段时间,而另一些函数正常运行。我一直用这个来玩纯JS游戏。
要使主线程忙上几毫秒,请执行以下操作:
1 2 3 4 | function wait(ms) { const start = performance.now(); while(performance.now() - start < ms); } |
我也有类似的问题,必须等待控制存在和检查间隔。由于JavaScript中没有真正的睡眠、等待或暂停,并且Internet Explorer中不支持使用wait/async,所以我使用setTimeout和注入函数来解决问题,以防成功找到元素。以下是完整的示例代码,因此每个人都可以复制并将其用于自己的项目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"> <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"); }); } </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> |
我使用多线程HTML5工作线程,它将能够中止指向无响应URL的同步XMLHttpRequest。这不会阻止浏览器。
https://gist.github.com/el-gringo/6990785
一种非常简单的睡眠方式,可以与任何运行javascript的程序兼容…这段代码已经用500个条目进行了测试,CPU和内存使用率在我的Web浏览器上仍然不可见。
这里有一个函数等待节点变为可见…
此函数创建一个新的上下文
1 2 3 4 5 6 7 8 9 10 11 12 | 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. }; |
我得到的承诺是不是一个使用顶级答案的构造函数。如果你进口蓝鸟,你可以这样做。最简单的解决方案。
1 2 3 4 | import * as Promise from 'bluebird'; await Promise.delay(5000) |
javascript函数不允许挂起。使用同步的javascript过程来实现。过程等待I/O操作和睡眠超时。可用于javascript 1.7。
演示:示范睡眠演示可暂停程序
使用实际睡眠函数的问题是,javascript是单线程的,睡眠函数会使您的浏览器选项卡在这段时间内挂起。
我有这个问题很长时间了,我需要的答案并不完全是这里提供的。此等待功能会导致同步等待,而不会占用CPU。waitforit向任何地方发出Ajax请求,并将异步标志设置为false。Waitf对一个帧做同样的操作,Waitd对一个分区做同样的操作。Ajax大约需要100毫秒,帧大约是25毫秒,而Div大约是1毫秒。等待功能根据您给它多少时间来利用所有这些功能。如果等待的时间不够长,那就再做一次。在处理多个异步加载元素时,我需要这样做。基本上是"等到这个元素存在"。你可以在这里玩https://jsfiddle.net/h2vm29ue/它只是利用了浏览器自然等待的东西。较长版本的https://jsfiddle.net/5cov1p0z/32/更精确。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 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); } |
简短的答案是否定的,而不是javascript本身。您的解决方案似乎是不将控制权返回到环境的唯一方法。
如果环境不支持事件,这是必需的。他们可能也不支持设置时间。
如果您处于事件驱动的环境中(如浏览器或node.js),那么setTimeout绝对是最好的方法。
在服务器端,可以使用deasync
例子:
1 2 3 4 5 6 7 8 | #!/usr/bin/env node // Requires `npm install --save deasync` var sleep = require("deasync").sleep; sleep(5000); console.log ("Hello World!!"); |
但是,如果您需要一个纯的javascript函数(例如,通过浏览器在客户端运行它),我很抱歉地说,我认为您的
这不仅会暂停您的函数,还会暂停整个事件循环。所以不会有其他活动。
它使您的CPU处于100%负载。
因此,如果您需要它作为一个浏览器脚本,并且不希望这些可怕的影响,我必须说您应该重新考虑您的功能:
A)您可以在超时时调用它(或调用
b)。或者,如果您需要等待结果,那么您应该使用承诺(或者回调地狱,当然;-)。
没有预期结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function myFunc() { console.log ("Do some things"); setTimeout(function doTheRest(){ console.log ("Do more things..."); }, 5000); // Returns undefined. }; myFunc(); |
返回承诺的示例(注意它会改变您的函数用法):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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(" ")); }, 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); }); |
在某些情况下,一个好的选择是显示一个顶级消息面板来停止用户交互,然后在得到等待的结果时(异步)再次隐藏它。这允许浏览器继续执行后台任务,但会暂停工作流,直到返回结果。
总结一下(就像前面的答案中所说的那样):
javascript中没有内置的睡眠功能。您应该使用setTimeout或setInterval来达到类似的效果。
如果你真的想,你可以用一个for循环来模拟睡眠功能,比如原始问题中所示的循环,但是这会让你的CPU疯狂地工作。在Web工作者内部,另一种解决方案是使同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 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!'); |
使用三种功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | var foo = {}; function main() { 'use strict'; /*Initialize global state*/ foo.bar = foo.bar || 0; /* Initialize timer */ foo.bop = setInterval(foo.baz, 1000); } sleep = function(timer) { 'use strict'; clearInterval(timer); timer = setTimeout(function(){main()}, 5000); }; foo.baz = function() { 'use strict'; /* Update state */ foo.bar = Number(foo.bar + 1) || 0; /* Log state */ console.log(foo.bar); /* Check state and stop at 10 */ (foo.bar === 5) && sleep(foo.bop); (foo.bar === 10) && clearInterval(foo.bop); }; main(); |
工具书类
使用javascript开发游戏
为什么iOS 8中的滚动事件更改是一件大事
ember.js中的实时投票系统
使用RequestAnimationFrame()驱动动画
mdn:javascript并发模型和事件循环
网格研究:node.js
在javascript中超过60fps
第2部分:不阻塞单线程的CPU密集型JavaScript计算
我更喜欢这种功能型的
1 2 | const sleep = ms => Promise(resolve => setTimeout(resolve, ms)) |
在LiveScript(编译为javascript)中,可以执行以下操作:
1 2 3 4 5 6 7 | sleep = (ms, func) -> set-timeout func, ms console.log"hello-1" <- sleep 2000ms console.log"hello-2" <- sleep 2000ms console.log"hello-3" |
另一种可能的方法是:
1 2 3 4 5 | var _timer; clearTimeout(_timer); _timer = setTimeout(function() { // Your code }, 1000); // Delay for 1 s. |
我最近有一个案例,在执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | var pause = false; function something() { if(edited && renamed) { pause = true; $.ui.popup({ //... doneCallback: function() { //... pause = false; } //... }); //... } var waitCode = setInterval(function(){ if(!pause) { //... code I needed to wait on ... clearInterval(waitCode); } },500); } |
我相信有无数种方法可以使这个更好,但我想我可以通过创建一个对象来尝试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | // execute code consecutively with delays (blocking/non-blocking internally) function timed_functions() { this.myfuncs = []; this.myfuncs_delays = []; // mirrors keys of myfuncs -- values stored are custom delays, or -1 for use default this.myfuncs_count = 0; // increment by 1 whenever we add a function this.myfuncs_prev = -1; // previous index in array this.myfuncs_cur = 0; // current index in array this.myfuncs_next = 0; // next index in array this.delay_cur = 0; // current delay in ms this.delay_default = 0; // default delay in ms this.loop = false; // will this object continue to execute when at end of myfuncs array? this.finished = false; // are we there yet? this.blocking = true; // wait till code completes before firing timer? this.destroy = false; // destroy self when finished this.next_cycle = function() { var that = this; var mytimer = this.delay_default; if(this.myfuncs_cur > -1) if(this.myfuncs_delays[this.myfuncs_cur] > -1) mytimer = this.myfuncs_delays[this.myfuncs_cur]; console.log("fnc:" + this.myfuncs_cur); console.log("timer:" + mytimer); console.log("custom delay:" + this.myfuncs_delays[this.myfuncs_cur]); setTimeout(function() { // times up! next cycle... that.cycle(); }, mytimer); } this.cycle = function() { // now check how far we are along our queue.. is this the last function? if(this.myfuncs_next + 1 > this.myfuncs_count) { if(this.loop) { console.log('looping..'); this.myfuncs_next = 0; } else this.finished = true; } // first check if object isn't finished if(this.finished) return false; // HANDLE NON BLOCKING // if(this.blocking != true) // blocking disabled { console.log("NOT BLOCKING"); this.next_cycle(); } // set prev = current, and current to next, and next to new next this.myfuncs_prev = this.myfuncs_cur; this.myfuncs_cur = this.myfuncs_next; this.myfuncs_next++; // execute current slot this.myfuncs[this.myfuncs_cur](); // HANDLE BLOCKING if(this.blocking == true) // blocking enabled { console.log("BLOCKING"); this.next_cycle(); } return true; }; // adders this.add = { that:this, fnc: function(aFunction) { // add to the function array var cur_key = this.that.myfuncs_count++; this.that.myfuncs[cur_key] = aFunction; // add to the delay reference array this.that.myfuncs_delays[cur_key] = -1; } }; // end::this.add // setters this.set = { that:this, delay: function(ms) { var cur_key = this.that.myfuncs_count - 1; // this will handle the custom delay array this.that.myfunc_delays // add a custom delay to your function container console.log("setting custom delay. key:"+ cur_key +" msecs:" + ms); if(cur_key > -1) { this.that.myfuncs_delays[cur_key] = ms; } // so now we create an entry on the delay variable }, delay_cur: function(ms) { this.that.delay_cur = ms; }, delay_default: function(ms) { this.that.delay_default = ms; }, loop_on: function() { this.that.loop = true; }, loop_off: function() { this.that.loop = false; }, blocking_on: function() { this.that.blocking = true; }, blocking_off: function() { this.that.blocking = false; }, finished: function(aBool) { this.that.finished = true; } }; // end::this.set // setters this.get = { that:this, delay_default: function() { return this.that.delay_default; }, delay_cur: function() { return this.that.delay_cur; } }; // end::this.get } // end:::function timed_functions() |
使用如下://////开始::测试//////
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | // initialize var fncTimer = new timed_functions; // set some defaults fncTimer.set.delay_default(1000); fncTimer.set.blocking_on(); // fncTimer.set.loop_on(); // fncTimer.set.loop_off(); // BEGIN :: ADD FUNCTIONS (they will fire off in order) fncTimer.add.fnc(function() { console.log('plan a (2 secs)'); }); fncTimer.set.delay(2000); // set custom delay for previously added function fncTimer.add.fnc(function() { console.log('hello world (delay 3 seconds)'); }); fncTimer.set.delay(3000); fncTimer.add.fnc(function() { console.log('wait 4 seconds...'); }); fncTimer.set.delay(4000); fncTimer.add.fnc(function() { console.log('wait 2 seconds'); }); fncTimer.set.delay(2000); fncTimer.add.fnc(function() { console.log('finished.'); }); // END :: ADD FUNCTIONS // NOW RUN fncTimer.cycle(); // begin execution // // // END :: TEST // // // |
现在还可以使用本机模块实用程序来提供常规同步功能。
1 2 3 4 5 6 7 8 | const { promisify } = require('util') const sleep = promisify(setTimeout) module.exports = () => { await someAsyncFunction() await sleep(2000) console.log('2 seconds later...') } |
这里有一种睡在
就一次,接近代码的开头
1 | var WSHShell = new ActiveXObject ("WScript.Shell"); |
如果睡眠时间为1秒=1000毫秒,则执行该语句
1 | WSHShell.Run ('Sleep.js 1000', 3, true); |
在与脚本相同的目录中是文件
1 | WScript.Sleep (WScript.Arguments (0)); |
(注意,
可能我有点晚了,有点懒惰,有点无聊,或者有点打扰人,或者有点像"大嘴巴后面",但是……
到目前为止,我读到的每一个解决方案都像是"让我们睡一觉,看看明天发生了什么"。
setInterval(callback,time)将等待很长时间,然后在阻塞运行时调用回调。"setinterval"的当前实现远不是线程保存,甚至不是考虑并发性。
虽然上面提到的稀疏解决方案看起来像,猜猜怎么着,C(笑),但它们在C/.NET中仍然不起作用。它们仍然像在C中一样工作。
Javascript目前不提供实现真正多线程的体系结构。最好的方法是使用typescript,但它仍然缺少一个真正的解决方案…伤害。javascript、jquery、ajax、jnode,甚至是typescript都只是一堆依赖于实现者情绪的好东西和坏东西的人。事实。完全停止。
这可能有效。它在C和JavaScript中为我工作。
1 2 3 4 | function sleep(time) { var x = 0; for(x = 0;x < time;x++) {/* Do nothing*/} } |
1 2 3 4 5 6 7 8 9 10 | var waitTillSomethingHappens = function(){ if(somethingHappened == 1) { alert('Something Happened get out of sleep'); } else { setTimeout(waitTillSomethingHappens,1000); } }; |
或者只是创建:
1 2 3 4 5 6 7 8 9 10 11 | function yourFunction(){ //do something setInterval(myFunc(),1000); //do something else } function myFunc(){ return; } |
这将只等待指定的间隔,并调用不做任何事情的函数。