JavaScript中的sleep()是什么?

3710

有没有比以下的pausecomp函数(取自这里)更好的方式在JavaScript中实现sleep?

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

这不是JavaScript中的休眠-操作之间的延迟的重复;我需要在函数执行过程中进行真正的休眠,而不是在某段代码执行之前延迟。


273
这是一个可怕的解决方案 - 在不做任何事情的同时,你将会消耗大量的处理周期。 - 17 of 26
21
睡眠的唯一目的就是轮询或等待回调 - setInterval 和 setTimeout 都比睡眠更有效。 - annakata
65
看到人们在不理解原帖想法的情况下就拒绝了,这真是令人惊讶。有时你需要一次__真正的睡眠__。我现在需要一次真正的睡眠来测试在顶层窗口和 iframe 之间发布和接收消息时浏览器的行为。用 while 循环来保持忙碌似乎是唯一的方法。 - Devs love ZenUML
2
我想使用 sleep() 来计时动画。(我知道有更好的方法...)提问者提供的代码在 Chrome 中对我无效,因为浏览器不会在脚本中进行修改后立即更新 DOM,而是等待代码执行完成后再进行任何 DOM 更新,因此脚本会等待所有延迟的总和,然后一次性应用所有 DOM 更新。 - Ari Fordsham
16
@DevsloveZenUML,浏览器环境的设计师和开发人员为了用户的利益决定不满足你们的要求,因为在异步应用程序中赋予某人阻止整个页面的明确能力是疯狂的。 - Oleg V. Volkov
显示剩余5条评论
94个回答

9
function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }
}

1
这与真正的问题相同。将其作为真正的答案并没有太多意义。 - EML
2
不是一个好的解决方案 - 在Selenium的JavaScriptExecutor中使用它会导致我的Chrome浏览器在2104 MacBook Pro上约50%的时间挂起。 - emery
需要解释一下。这个想法/主旨是什么?它与以前的答案有何不同? - Peter Mortensen

9
你可以这样做。创建一个所有函数都可以继承的 sleep 方法:

Function.prototype.sleep = function(delay, ...args) {
    setTimeout(() => this(...args), delay)
}

console.log.sleep(2000, 'Hello, World!!')


3
目前为止,这是我最喜欢的。 - pasha
1
这是最好的解决方案!没有繁忙等待,并且您可以将其附加到任何函数上,在执行该函数之前引起延迟。 - Akumaburn

9

如果你需要处理同步执行,我可以理解睡眠函数的用途。setInterval 和 setTimeout 函数会创建一个并行执行线程,将执行顺序返回到主程序,如果你必须等待特定结果,则效率不高。当然,你可以使用事件和处理程序,但在某些情况下,这不是预期的结果。


8
如果你使用Node.js,可以查看fibers - 一种原生的C扩展,用于模拟多线程。
它允许您以阻塞纤维的方式进行真正的sleep,但在主线程和其他纤维中是非阻塞的。
以下是他们自己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');

– 结果如下:

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

8

可以使用Java的sleep方法来完成。我在Firefox和Internet Explorer中进行了测试,它不会锁定计算机,消耗资源或导致无尽的服务器访问。对我来说,这似乎是一个干净的解决方案。

首先您需要加载Java并使其方法可用于页面。为此,我执行了以下操作:

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

那么,当您想在JavaScript代码中进行无痛暂停时,您需要做的就是:

java.lang.Thread.sleep(xxx)

这里的 xxx 是以毫秒为单位的时间。在我的情况下(为了解释),这是一个非常小的公司后端订单处理的一部分,我需要打印从服务器加载的发票。我通过将发票(作为网页)加载到 iFrame 中,然后打印 iFrame 来完成此操作。

当然,在打印之前,我必须等待页面完全加载,因此 JavaScript 代码必须暂停。我通过使发票页面(在 iFrame 中)使用 onLoad 事件更改父页面上的隐藏表单字段来实现这一点。而打印发票的父页面代码如下(无关部分已剪切以增加清晰度):

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

所以用户按下按钮后,脚本会加载发票页面,并等待每0.25秒检查发票页面是否加载完成,然后弹出打印对话框供用户将其发送到打印机。QED。


13
考虑到作者想要实现的简单目标,这个解决方案似乎过于复杂。 - xaralis
3
这取决于已被弃用的Java小程序。 - Vahid Amiri

8

在JavaScript中,你不能像这样进行睡眠,或者说,你不应该这样做。运行睡眠或while循环将导致用户的浏览器挂起,直到循环完成。

像你引用的链接中所指定的那样,使用一个计时器。


7

如果想要在循环中执行一组调用时间隔一段时间,您可以使用类似下面代码的原型。没有原型,则可以使用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)
}

7

如果您有一个响应用户点击的函数,最终会打开一个新的弹出窗口,并且您已经启动了一些需要在弹出窗口显示之前完成的处理,则可能需要使用sleep()函数而不是使用setTimeout()。将打开窗口移动到闭包中意味着它通常会被浏览器阻塞。


7
一个古老的问题来自2009年。现在2015年,使用ECMAScript 2015,也称为ES6,可以实现一个新的解决方案。该技术于2015年6月获得批准,但在此之前Firefox和Chrome已经实现了该技术。现在,可以制作一个非忙碌、非阻塞并嵌套在循环和子函数内部的睡眠函数,而不会冻结浏览器。只需要纯JavaScript即可 - 不需要库或框架。
下面的程序展示了如何创建sleep()runSleepyTask()函数。 sleep()函数只是一个yield语句。实际上,直接在wakeup()中写yield语句比调用sleep()函数更容易,但是这样就没有睡眠词了 :-)。 yield返回一个时间值到wakeup()中的next()方法,并等待。实际的“睡眠”是通过setTimeout()wakeup()中完成的。在回调中,next()方法触发yield语句继续执行,而yield的"神奇"之处在于所有局部变量和整个调用堆栈都保持不变。
使用sleep()或yield的函数必须定义为生成器。将一个星号添加到关键字function*即可轻松完成。要执行生成器有点棘手。使用关键字new调用时,生成器返回一个具有next()方法的对象,但不会执行生成器的主体(关键字new是可选的,没有区别)。 next()方法触发生成器主体的执行,直到遇到yield。包装函数runSleepyTask()启动乒乓: next()等待yield,而yield则等待next()
另一种调用生成器的方式是使用关键字yield*,这里它就像一个简单的函数调用,但它还包括向next()回传yield的能力。
所有这些都可以通过示例drawTree()来演示。它在旋转的3D场景中绘制带叶子的树。树被绘制为干树并在其顶部以不同方向分成3个部分。然后,每个部分都作为另一个更小的树递归地调用drawTree()后进行短暂的睡眠。非常小的树仅绘制为叶子。
每个叶子都在runSleepyTask()中启动的单独任务中拥有自己的生命。它们在growLeaf()中诞生、生长、坐、褪色、落下并死亡。速度由sleep()控制。这演示了多任务处理是多么容易。

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>

3D相关的内容被隐藏在setup3D()中,它只是为了使操作比console.log()不那么无聊。顺便说一下,角度以弧度表示。

已经测试过在Firefox和Chrome中可以正常工作。但在Internet Explore和iOS(iPads)中未实现。请尝试自己运行。

在仔细查看答案后,我发现Gabriel Ratener于一年前回答了类似的问题(对于JavaScript版本的sleep()是什么?)


7
在 sleep 方法中,你可以返回任何可 then 的对象,而不一定是一个新的 Promise 对象。
例如:

const sleep = (t) =>  ({ then: (r) => setTimeout(r, t) })

const someMethod = async () => {

    console.log("hi");
    await sleep(5000)
    console.log("bye");
}

someMethod()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接