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个回答

2

如果您希望避免性能下降,setTimeout 是您期望的 sleep函数。但是,如果您想要一种语法,在此语法中代码被sleep“分成两部分”,我们可以这样做:

sleep = function(tm, fn){
   window.setTimeout(fn, tm);
}

那么,请按照以下方式准备函数:
var fnBeforeSleep = function(){

  // All code before sleep

}

var fnAfterSleep = function(){

  // All code after sleep

}

然后:

fnBeforeSleep();
sleep(2000, fnAfterSleep);

// Yep! Syntactically, it is very close to:

fnBeforeSleep();
sleep(2000);
fnAfterSleep();

2
您可以使用闭包调用 setTimeout() 并逐渐增加值。
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);
});

结果:

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
这是一种很好的方法,可以在缓存之前限制对API的调用。 (在我的情况下,我向Google Geocode API发送了一些重复的邮政编码并缓存了结果,但存在两个问题。循环执行得非常快,以至于第一个API响应仍在返回,而Google Geocode API有一个速率限制器,会开始拒绝请求)。 - chim
简而言之,在循环上下文中工作时,您需要执行类似于tponthieux答案的操作。 (例如使用myArray.forEach或for(i = 1; ...))。 - chim
1
我们应该让这个答案得到投票支持,因为它很有用,并且提供了与其他数百万个回答不同的东西。 - chim
你能详细说明一下“闭包调用setTimeout()”吗?为什么要使用“逐渐增大的值”? - Peter Mortensen

2
我知道这个问题是关于睡眠的,很明显答案是不可能的。我认为睡眠的一个常见需求是按顺序处理异步任务;我知道我肯定不得不处理它。
在许多情况下,可以使用承诺(常用于Ajax请求)。它们让您以同步方式执行异步操作。还有成功/失败处理,它们可以链接在一起。
它们是ECMAScript 6的一部分,因此浏览器支持并不完整,主要是Internet Explorer不支持它们。还有一个名为Q的库可用于执行承诺。
参考资料:

这应该是一条评论,而不是一个答案。 - Aadit M Shah

2
如果你想要在所有浏览器上都可用的代码,那么使用 setTimeout()clearTimeout()。如果你一直阅读到这里,你可能会注意到已接受的答案在 Internet Explorer 11 中破坏了所有 JavaScript 编译,并且在使用此解决方案后,大约有5%的用户仍在使用这个活跃开发的浏览器并需要支持。
这几乎破坏了所有东西。已知有关于箭头函数破坏DrupalWordPressAmazon AWSIBM的功能,甚至在Stack Overflow上有一个专门的讨论

你可以去看一下...

Browser Compatibility Chart - Arrow Function Expressions

Browser Compatibility Chart - setTimeout

使用 setTimeout()clearTimeout(),这将适用于所有浏览器...

工作中的 JSBin 演示

var timeout;

function sleep(delay) {
    if(timeout) {
        clearTimeout(timeout);
    }
    timeout = setTimeout(function() {
        myFunction();
    }, delay);
}

console.log("sleep for 1 second");
sleep(1000);

function myFunction() {
    console.log("slept for 1 second!");
}


2

使用同步调用让操作系统进行睡眠的函数。可以使用任何操作系统的睡眠命令。它不是在使用CPU时间的意义上忙等待。

我选择了对一个不存在的地址进行ping测试。

const cp = require('child_process');

function sleep(ms)
{
    try{cp.execSync('ping 192.0.2.0 -n 1 -w '+ms);}
    catch(err){}
}

进行验证它是否工作的测试

console.log(Date.now());
console.log(Date.now());
sleep(10000);
console.log(Date.now());
console.log(Date.now());

还有一些测试结果。

1491575275136
1491575275157

(10秒后)
1491575285075
1491575285076

但它在Web浏览器上下文中无法工作。它适用于哪种上下文? - Peter Mortensen

1
如果你想让一个匿名函数像你创建的处理程序一样休眠,我建议采用以下方法:
function()
{
    if (!wait_condition)
    {
        setTimeout(arguments.callee, 100, /* Comma-separated arguments here */);
    }
    // The rest of the function
}

这段代码的意思是:“如果等待条件尚未满足,请使用这些参数再次调用此函数。” 我已经使用了这种方法将相同的参数传递给我的处理程序,有效地使这段代码成为非轮询的sleep()(仅在函数开头起作用)。

1
一个需要使用“sleep”方法的对象的方法如下所示:
function SomeObject() {
    this.SomeProperty = "xxx";
    return this;
}
SomeObject.prototype.SomeMethod = function () {
    this.DoSomething1(arg1);
    sleep(500);
    this.DoSomething2(arg1);
}

几乎可以翻译成:

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”方法不存在,我使用后一种方法并相应地设计我的代码。


1

有一个新的库,Sequencr.js,它可以通过设置延迟时间来整洁地链接函数,以避免回调地狱。

它可以将以下代码转换为:

setTimeout(function(timeout){
    function1();
    setTimeout(function(timeout){
        function2();
        setTimeout(function(timeout){
            function3();
        }, timeout, timeout)
    }, timeout, timeout)
}, 10, 10);

转化为:

Sequencr.chain([function1, function2, function3], 10);

并且内置了支持在每次迭代之间“休眠”的循环。

1
大多数解决方案的问题在于它们会倒回堆栈。在某些情况下,这可能是一个大问题。在此示例中,我展示了如何以不同的方式使用迭代器来模拟“真正的休眠”。
在此示例中,生成器调用自己的next(),因此一旦它开始运行,它就独立运行。
var h = a();
h.next().value.r = h; // That's how you run it. It is the best I came up with

// Sleep without breaking the 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

使用TypeScript:

这是一个可以等待的快速sleep()实现。它尽可能地与顶部答案相似。它在功能上是等效的,除了在TypeScript中将ms类型定义为number

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

async function demo() {
  console.log('Taking a break for 2s (2000ms)...');
  await sleep(2000);
  console.log('Two seconds later');
}

demo();

这就是它:await sleep(<duration>)

请注意:

  1. await 只能在带有 async 关键字的函数中执行,或者在某些环境(例如 Chrome DevTools 控制台或 Runkit)中的脚本的 顶层
  2. await 仅暂停当前的 async 函数。

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