有没有比下面的pausecomp函数(取自此处)更好的方法来设计JavaScript中的睡眠?

function pausecomp(millis)
{
    var date = new Date();
    var curDate = null;
    do { curDate = new Date(); }
    while(curDate-date < millis);
}

这不是JavaScript中的Sleep的重复-动作之间的延迟;我希望在函数的中间有一个真正的睡眠,而不是在代码执行之前有一段延迟。


当前回答

2009年的一个老问题。2015年,ECMAScript 2015 AKA ES6中定义的生成器可以提供新的解决方案。它于2015年6月获得批准,但之前已在Firefox和Chrome中实现。现在,休眠功能可以在不冻结浏览器的情况下,在循环和子函数中进行非繁忙、非阻塞和嵌套。只需要纯JavaScript,不需要库或框架。

下面的程序显示了sleep()和runSleepyTask()是如何实现的。sleep()函数只是一个yield语句。它非常简单,实际上直接编写yield语句比调用sleep()更容易,但这样就不会有sleep单词了:-)yield返回一个时间值给wakeup()内的next()方法并等待。实际的“睡眠”是使用旧的setTimeout()在wakeup()中完成的。在回调时,next()方法触发yield语句继续,yield的“魔力”在于所有局部变量及其周围的整个调用堆栈仍然完好无损。

使用sleep()或yield的函数必须定义为生成器。这很容易通过在关键字函数*中添加星号来实现。执行生成器有点棘手。当使用关键字new调用时,生成器返回一个具有next()方法的对象,但不执行生成器的主体(关键字new是可选的,没有任何区别)。next()方法触发生成器主体的执行,直到遇到产量。包装函数runSleepyTask()启动乒乓球:next()等待yield,yield等待next(()。

另一种调用生成器的方法是使用关键字yield*,这里它的工作方式类似于一个简单的函数调用,但它还包括返回next()的能力。

示例drawTree()演示了这一切。它在旋转的3D场景上绘制了一棵带有树叶的树。一棵树被画成树干,顶部有三个不同方向的部分。在短暂的睡眠后,通过递归调用drawTree(),将每个部分绘制为另一个较小的树。一棵很小的树被画成一片叶子。

每个叶子在一个单独的任务中都有自己的生命,该任务以runSleepyTask()开始。它在growtLeaf()中出生、生长、静止、褪色、坠落和死亡。速度由sleep()控制。这证明了多任务处理是多么容易。

函数*sleep(毫秒){yield毫秒};函数runSleepyTask(任务){(函数唤醒(){var result=task.next();if(!result.done)setTimeout(唤醒,result.value);})()}////////////////作者:Ole Middelboe/////////////////////////////pen3D=setup3D();var taskObject=新绘图树(pen3D.center,5);runSleepyTask(taskObject);函数*drawTree(root3D,大小){如果(size<2)运行SleepyTask(new-growLeaf(root3D))其他{pen3D.drawTrunk(root3D,大小);for(变量p为[1,3,5]){var part3D=新pen3D.Thing;root3D.add(part3D);part3D.移动(尺寸).转动(p).倾斜(1-p/20);产量*睡眠(50);产量*drawTree(part3D,(0.7+p/40)*尺寸);}}}函数*growtLeaf(stem3D){var leaf3D=pen3D.drawLeaf(stem3D);对于(var s=0;s++<15;){yield*sleep(100);leaf3D.scale.multiplyScalar(1.1)}yield*sleep(1000+9000*Math.random());对于(var c=0;c++<30;){yield*sleep(200);leaf3D.sskin.color.setRGB(c/30,1-c/40,0)}对于(var m=0;m++<90;){yield*sleep(50);leaf3D.turn(0.4).tilt(0.3).move(2)}leaf3D.visible=false;}///////////////////////////////////////////////////////////////////////函数setup3D(){var场景,相机,渲染器,directionalLight,pen3D;scene=新的THREE.scene();相机=新的三个透视相机(75,window.innerWidth/window.innerHeight,0.1,1000);相机位置设置(0,15,20);renderer=new THREE.WebGLRenderer({alpha:真,抗锯齿:真});renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(render.domElement);directionalLight=新的三个。directionalLight(0xffffaa,0.7);directionalLight.position.set(-1,2,1);scene.add(directionalLight);scene.add(新的THREE.AmbientLight(0x9999ff));(函数render(){requestAnimationFrame(渲染);//renderer.setSize(window.innerWidth,window.innerHeight);场景旋转Y(10/60/60);renderer.render(场景,摄影机);})();window.addEventListener窗口('调整大小',函数(){renderer.setSize(window.innerWidth,window.innerHeight);camera.aspx ect=window.innerWidth/window.innerHeight;camera.updateProjectionMatrix();},假的);pen3D={drawTrunk:函数(根,大小){//root.skin=皮肤(0.5、0.3、0.2);root.add(新的三个网格(新的三维圆柱体几何体(大小/12,大小/10,大小16),root.skin).translateY(大小/2));root.add(新的THREE.Mesh(新的THEREE.SphereGeometry(size/12,16),root.skin).translateY(大小));返回根;},drawLeaf:功能(茎){stem.skin.color.setRGB(0,1,0);stem.add(新的三个网格(新的三维圆柱体几何体(0,0.02,0.6),茎.皮肤).旋转X(0.3).平移Y(0.3));stem.add(新的THREE.Mesh(新的THREE.CircleGeometry(0.2),茎.皮肤).旋转X(0.3).平移Y(0.4));返回阀杆;},事物:函数(){三.Object3D.调用(this);this.skin=新的THREE.MeshLambertMaterial({颜色:新三。颜色(0.5,0.3,0.2),vertexColors:THREE.FaceColors(顶点颜色),边:三边,双面})}};pen3D.Thing.prototype=对象创建(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=新pen3D.Thing;scene.add(pen3D.center);返回pen3D;}<script src=“https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js“></script>

3D内容隐藏在setup3D()中,只是为了让它比console.log()少一些无聊。顺便说一下,角度是用弧度来测量的。

经测试可在Firefox和Chrome中运行。未在Internet Explorer和iOS(iPad)中实施。试着自己运行它。

在我再次找到答案后,加布里埃尔·拉特纳一年前对sleep()的JavaScript版本是什么做出了类似的回答?。

其他回答

我也遇到过类似的问题,必须等待控件的存在并每隔一段时间进行检查。由于JavaScript中没有真正的休眠、等待或暂停,并且Internet Explorer中不正确支持使用wait/async,因此我使用setTimeOut并注入函数以成功找到元素。以下是完整的示例代码,因此每个人都可以复制并将其用于自己的项目:

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

JavaScript函数不允许任何暂停。使用同步JavaScript,可以实现过程。过程等待I/O操作和睡眠超时。它可用于JavaScript 1.7。

演示:

演示睡眠演示可暂停程序

你可以这样做。所有函数都可以继承的休眠方法:

Function.prototype.sleep=函数(延迟,…args){setTimeout(()=>this(…args),delay)}console.log.sleep(2000年,“你好,世界!!”)

这可能会奏效。它在C和JavaScript中对我有用。

function sleep(time) {
  var x = 0;
  for(x = 0;x < time;x++) {/* Do nothing */}
}

我认为这个问题很好,指出了重要的观点和考虑因素。

话虽如此,我认为问题的核心在于意图和理解开发人员(您)想要控制什么。

首先,名称sleep是一个重载的命名选择。即,“什么”将被“睡眠”;作为一名开发人员,我控制着什么?

在任何语言引擎中,在任何OS进程上运行,在任何裸机或托管系统上,“开发人员”都不控制(拥有)OS共享资源CPU内核(和/或线程),除非他们正在编写OS/process系统本身。CPU是一种时间共享资源,工作执行进度的货币是分配给系统上要执行的所有工作的“周期”。

作为一名应用程序/服务开发人员,最好考虑我是由操作系统进程/语言引擎管理的工作流活动流的控制者。在一些系统上,这意味着我控制一个本机os线程(可能共享CPU内核),在其他系统上,意味着我可以控制一个异步延续工作流链/树。

对于JavaScript,它是“后者”。

因此,当需要“睡眠”时,我打算让我的工作流在执行过程中“延迟”一段时间,然后再继续执行工作流中的下一个“步骤”(阶段/活动/任务)。

这是“恰当”的说法,即作为一个开发人员,最容易将模型作为线性代码流来工作;根据需要采用工作流的组合来进行缩放。

今天,在JavaScript中,我们可以选择使用高效的多任务20世纪80年代基于角色的延续架构(重新标记为现代Futures/Promises/then/await等)来设计这样的线性工作流。

考虑到这一点,我的答案不是提供新的技术解决方案,而是关注问题本身的意图和设计视角。

我建议,任何答案都要从思考上述概念开始,然后选择一个提醒和暗示意图的名字(而不是睡眠)。

工作流选择1:delayWorkForMs(nMsToDelay)选项2:delaySyncSequenceForMs(msPeriod)

async delayAsyncSequenceForMs(msPeriod) {
  await new Promise(resolve => setTimeout(resolve, msPeriod));
}

请记住,任何异步函数都会返回Promise,await只能在异步函数中使用。(哈哈,你可能会问自己为什么……)。注意事项1:不要使用“循环”来消耗cpu周期。注意事项2:在JavaScript模型中,当在非异步函数中时,您不能“延迟”(等待)“异步”工作流的执行(除非您正在做不必要的坏事,而不需要占用cpu周期)。您只能在“异步”函数中“延迟”代码步骤。在内部,“异步”函数被建模为每个await关键字处的入口点/延续的集合。如果您熟悉倒勾插值模型,您可以“将await”视为在概念上建模,类似于编写倒勾字符串,如:

  // Conceptualizing, using an interpolation example to illustrate
  // how to think about "await" and "async" functions
  `code${await then-restart-point}more-code${await then-restart-point}`