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

1
这将为你解决问题。
var reloadAfter = 10; //seconds
var intervalId = setTimeout(function() {
    //code you want to execute after the time waiting
}, reloadAfter * 1000); // 60000 = 60 sec = 1 min

1
这与之前的答案有何不同? - Peter Mortensen

1

总结一下(正如之前的答案所说):

JavaScript 中没有内置的睡眠功能。您应该使用 setTimeoutsetInterval 来实现类似的效果。

如果您真的想要,您可以通过像原始问题中显示的那样使用 for 循环来模拟睡眠功能,但这会让您的 CPU 疯狂工作。在 Web Worker 内部,另一种解决方案是对不响应的 IP 地址进行同步的 XMLHttpRequest 并设置适当的超时时间。这将避免 CPU 利用率问题。以下是一个代码示例:

// 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!');


2
美好但不再可能:睡眠1秒钟... VM1537 js:17 Uncaught InvalidAccessError: 无法在文档中发出同步请求时设置超时属性:超时无法设置。sleep @ VM1537 js:17(anonymous function) @ VM1537 js:26 - Red Pill

1

JavaScript 版本的 sleep() 是什么?

这个问题已经在当前被接受的答案中回答了:

await new Promise(r => setTimeout(r, 1000));

同时运行两个异步函数

将它放在一个名为sleep()的函数中,然后使用await sleep()是个好主意。
为了使用它,需要一些上下文:

function sleep (ms) { return new Promise(r => setTimeout(r, ms)); }

(async function slowDemo () {
  console.log('Starting slowDemo ...');
  await sleep(2000);
  console.log('slowDemo: TWO seconds later ...');
})();

(async function fastDemo () {
  console.log('Starting fastDemo ...');
  await sleep(500);
  for (let i = 1; i < 6; i++) {
    console.log('fastDemo: ' + (i * 0.5) + ' seconds later ...');
    await sleep(500);
  }
})();
.as-console-wrapper { max-height: 100% !important; top: 0; }

两个异步调用按顺序运行 - 一个接一个

但是假设slowDemo产生了fastDemo依赖的某些结果.
在这种情况下,slowDemo必须完成运行才能启动fastDemo

function sleep (ms) { return new Promise(r => setTimeout(r, ms)); }

(async () => {
  await (async function slowDemo () {
    console.log('Starting slowDemo ...');
    await sleep(2000);
    console.log('slowDemo: TWO seconds later ... completed!');
  })();

  (async function fastDemo () {
    console.log('Starting fastDemo ...');
    await sleep(500);
    let i = -2;
    for (i = 1; i < 5; i++) {
      console.log('fastDemo: ' + (i * 0.5) + ' seconds later ...');
      await sleep(500);
    }
    console.log('fastDemo: ' + (i * 0.5) + ' seconds later. Completed!');
  })();
})();
.as-console-wrapper { max-height: 100% !important; top: 0; }


1

如果您需要等待很多秒钟,那么使用async/awaitsetTimeout的当前解决方案是完美的。然而,如果您将其用于屏幕动画,则应该真正使用requestAnimationFrame()。这个函数与setTimeout非常相似,但是只有在动画对用户可见时才调用回调函数。这意味着,如果您在网站上运行动画并且用户切换选项卡,则动画将暂停并节省电池寿命。

以下是使用requestAnimationFrame实现wait方法的示例。它接受一定数量的帧,并在所有帧都经过后解析:

const waitFrames = (frames) => 
  new Promise((resolve) => {
    let framesPassed = 0;
    requestAnimationFrame(function loop() {
      if (++framesPassed >= frames) return resolve();
      requestAnimationFrame(loop);
    });
  });
  
// typewriter effect for demonstration
const content = document.querySelector(".content");

async function typeWriter(endText, frames) {
  content.textContent = "";
  for (const letter of endText) {
    content.textContent += letter;
    await waitFrames(frames);
  }
}

typeWriter("Okay. This simple typewriter effect is an example of requestAnimationFrame.", 8);
<p>
  The animation will play below; Try switching tabs and see that   
  the animation pauses.
</p>
<code class="content"></code>

了解更多关于requestAnimationFrame的内容

浏览器支持(IE10+)


1
如果您真的想完全阻止主线程并防止事件循环从事件队列中拉取,这里有一个不错的方法可以做到这一点,而无需创建任何函数、新的日期对象或泄漏任何变量。我知道这个愚蠢的问题已经有了无数答案,但我没有看到有人使用这个精确的解决方案。这仅适用于现代浏览器。
警告:这不是您会在生产环境中使用的内容。它只是有助于理解浏览器事件循环。它可能甚至对任何测试都没有用处。它不像普通的系统休眠函数,因为JavaScript运行时仍然在每个周期执行工作。
for (let e = performance.now() + 2000; performance.now() < e; ) {}

在这里使用setTimeout回调,即使它几乎立即进入事件队列,也要等待至少两秒钟才会被调用:

setTimeout(function() {
  console.log("timeout finished");
}, 0);

for (let e = performance.now() + 2000; performance.now() < e; ) {}
console.log("haha wait for me first");

您将会经历大约两秒钟的暂停,然后看到:
haha wait for me first
timeout finished

使用performance.now()而不是Date.now()的好处在于Date对象受到时钟偏移和系统时钟调整的影响。时间值可能并不总是单调递增,后续值可能会减少或保持不变。* 一般来说,performance.now()更适合在高精度下测量时间差异。
使用for循环的好处在于可以在运行前设置变量并将其局限于块内。这使您可以在循环外执行加法运算,同时仍然是“一行代码”。这应该能够最小化热循环负载的CPU负荷。

1
你可以使用Promise和setTimeout的组合:
const sleep = (ms) => {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

然后使用 await:
await sleep(3000);

我特意使用了一个更长的函数版本,以便更容易理解,但它可以更简洁地编写:
const sleep = (ms) => {
    return new Promise(resolve => setTimeout(resolve, ms));
}

这将实现与其他语言中的sleep()类似的结果,同时CPU使用率较低。

0
在服务器端,您可以使用deasyncsleep()方法,该方法是在C中本地实现的,因此它可以有效地实现等待效果,而不会阻塞事件循环或使CPU负载达到100%。
例如:
#!/usr/bin/env node

// Requires `npm install --save deasync`
var sleep = require("deasync").sleep;

sleep(5000);

console.log ("Hello World!!");

但是,如果您需要一个 JavaScript 函数(例如,在浏览器端运行它),我很抱歉地说,我认为您的 pausecomp() 函数是唯一的方法,而且更重要的是:

  1. 它不仅暂停了您的函数,还暂停了整个事件循环。因此,没有其他事件会被处理。

  2. 它使您的 CPU 负载达到 100%。

因此,如果您需要在浏览器脚本中使用它,并且不想出现这些可怕的影响,我必须告诉您应该重新考虑一下您的函数:

a). 您可以在超时后重新调用它(或调用一个 do_the_rest() 函数)。如果您不期望从函数中得到任何结果,则这是最简单的方法。

b). 或者,如果您需要等待结果,那么您应该转而使用 promises(或回调地狱,当然 ;-))。

不需要结果的示例:

function myFunc() {

    console.log ("Do some things");

    setTimeout(function doTheRest(){
        console.log ("Do more things...");
    }, 5000);

    // Returns undefined.
};

myFunc();

一个返回 Promise 的示例(注意它会改变你的函数用法):

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

0

如果你真的想要暂停一个脚本,你可以这样做:

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

如果您使用循环,那么这可能正是您想要的,您可以以各种方式更改它,以便您拥有伪多线程,在其中一些函数等待一段时间而其他函数正常运行。我经常在纯JavaScript游戏中使用它。


0

2021+ 更新

如果你正在寻找以下替代方案:

let sleep = ms => new Promise(res=>setTimeout(res,ms));

然后使用这个:

let sleep = async ms => void await Atomics.waitAsync(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms).value;

请注意,截至本问题发布时,它是一个第三阶段的提案。此外,它可能需要您的网站进行跨源隔离。要查看它是否在您的浏览器中工作,请尝试在 Stack Overflow 上执行以下操作:

let sleep = async ms => void await Atomics.waitAsync(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms).value;

void async function() {
  console.log(1);
  await sleep(2000);
  console.log(2);
}()


3
setTimeout相比,这种方法的优势是什么? - mousetail
@mousetail,精度可以达到不到一毫秒。在浏览器中,setTimeout可能会偏差100毫秒,特别是在高隐私设置下的Firefox。 - Ray Foss

0
为了让主线程忙碌几毫秒:
function wait(ms) {
  const start = performance.now();
  while(performance.now() - start < ms);
}

1
忙等待?之前的回答已经涵盖了这个问题了吧? - Peter Mortensen

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