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

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

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


当前回答

2019更新使用Atomics.wait

它应该在Node.js 9.3或更高版本中工作。

我在Node.js中需要一个非常精确的计时器,它非常适合。

然而,浏览器中的支持似乎非常有限。

设ms=10000;Atomics.wait(新Int32Array(新SharedArray Buffer(4)),0,0,ms);

运行了几次10秒计时器基准测试。

使用setTimeout,我得到的错误高达7000微秒(7毫秒)。

使用Atomics,我的错误似乎保持在600微秒(0.6毫秒)以下

2020年更新:总结

function sleep(millis){ // Need help of a server-side page
  let netMillis = Math.max(millis-5, 0); // Assuming 5 ms overhead
  let xhr = new XMLHttpRequest();
  xhr.open('GET', '/sleep.jsp?millis=' + netMillis + '&rand=' + Math.random(), false);
  try{
    xhr.send();
  }catch(e){
  }
}

function sleepAsync(millis){ // Use only in async function
  let netMillis = Math.max(millis-1, 0); // Assuming 1 ms overhead
  return new Promise((resolve) => {
    setTimeout(resolve, netMillis);
  });
}
function sleepSync(millis){ // Use only in worker thread, currently Chrome-only
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, millis);
}

function sleepTest(){
  console.time('sleep');
  sleep(1000);
  console.timeEnd('sleep');
}

async function sleepAsyncTest(){
  console.time('sleepAsync');
  await sleepAsync(1000);
  console.timeEnd('sleepAsync');
}

function sleepSyncTest(){
  let source = `${sleepSync.toString()}
    console.time('sleepSync');
    sleepSync(1000);
    console.timeEnd('sleepSync');`;
  let src = 'data:text/javascript,' + encodeURIComponent(source);
  console.log(src);
  var worker = new Worker(src);
}

其中服务器端页面,例如sleep.jsp,看起来像:

<%
try{
  Thread.sleep(Long.parseLong(request.getParameter("millis")));
}catch(InterruptedException e){}
%>

其他回答

干得好。正如代码所说,不要做一个糟糕的开发人员,在网站上使用它。这是一个开发实用函数。

// 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) {}
}

2017-2021更新

自2009年提出这个问题以来,JavaScript已经有了很大的发展。所有其他答案现在都过时或过于复杂。以下是当前的最佳实践:

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

或作为一个内衬:

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

作为一项功能:

const sleep = ms => new Promise(r => setTimeout(r, ms));

或使用字体:

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

将其用作:

await sleep(<duration>);

演示:

功能睡眠(ms){return new Promise(resol=>setTimeout(resolve,ms));}异步函数demo(){for(设i=0;i<5;i++){console.log(`等待${i}秒…`);等待睡眠(i*1000);}console.log('Done');}demo();

注意,

await只能在前缀为async关键字的函数中执行,或者在越来越多的环境中在脚本的顶层执行。await只暂停当前的异步函数。这意味着它不会阻止脚本的其余部分的执行,这在大多数情况下都是您想要的。如果您确实需要一个阻塞构造,请使用Atomics.wait查看这个答案,但请注意,大多数浏览器都不允许在浏览器的主线程上使用它。

两个新的JavaScript功能(截至2017年)帮助编写了这个“睡眠”函数:

Promises是ES2015(又称ES6)的原生功能。我们还在睡眠函数的定义中使用箭头函数。async/await特性允许代码显式地等待承诺(解决或拒绝)。

兼容性

承诺在Node v0.12+中得到支持,在浏览器中得到广泛支持,IE除外async/await登陆V8,自Chrome 55(2016年12月发布)以来默认启用它于2016年10月登陆Node 7并于2016年11月登陆Firefox Nightly

如果出于某种原因,您使用的Node早于7(已于2017年到期),或者针对的是旧浏览器,则仍可通过Babel(一种将JavaScript+新功能转换为普通旧JavaScript的工具)使用async/await,并使用将async-to-generator转换为生成器插件。

如果您真的想完全阻塞主线程并防止事件循环从事件队列中拉出,那么这里有一个很好的方法可以做到这一点,而不需要创建任何函数、新的Date对象或泄漏任何变量。我知道这个愚蠢的问题已经有一百万个答案了,但我没有看到有人使用这个精确的解决方案。这仅适用于现代浏览器。

警告:这不是你会投入生产的东西。它只是有助于理解浏览器事件循环。它可能对任何测试都没有用处。它不像一个正常的系统睡眠函数,因为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

在Date.now()上使用performance.now(()的好处是Date对象是

受到时钟偏斜和系统时钟调整的影响。这个时间的价值可能不总是单调增加随后的值可以减小或保持不变。*

通常,performance.now()更适合高精度地测量时间差异。

使用for循环的好处是可以在运行之前设置块的本地变量。这允许您在循环外进行加法运算,同时仍然是“单行”。这应该可以最大限度地减少这种热循环燃烧的CPU负载。

简短的答案是否定的,而不是JavaScript本身。您的解决方案似乎是不让控制权回归环境的唯一途径。

如果环境不支持事件,这是必要的。他们可能也不支持setTimeout。

如果您处于事件驱动环境(如浏览器或Node.js)中,setTimeout绝对是最好的方法。

使用TypeScript:

这里有一个可以等待的快速sleep()实现。这与上面的答案尽可能相似。它在功能上是等效的,除了ms被键入为TypeScript的数字。

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只能在前缀为async关键字的函数中执行,或者在某些环境中在脚本的顶层执行(例如,Chrome DevTools控制台或Runkit)。await只暂停当前的异步函数。