有没有比下面的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){}
%>

其他回答

一种可能需要sleep()函数而不是使用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只暂停当前的异步函数。

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

这里是使用requestAnimationFrame的wait方法的实现。它接收多个帧,并在它们全部通过后解析:

常量动画等待=(帧)=>新承诺((决心)=>{让framesPassed=0;requestAnimationFrame(函数循环(){如果(++framesPassed>=帧)返回resolve();requestAnimationFrame(循环);});});//演示用打字机效果const content=document.querySelector(“.content”);异步函数类型Writer(endText,等待){content.textContent=“”;for(const letter of endText){content.textContent+=字母;wait animationWait(等待);}}typeWriter(“好的。这个简单的打字机效果是requestAnimationFrame的一个例子。”,8);<p>动画将在下面播放;尝试切换选项卡并查看动画暂停。</p><code class=“content”></code>

阅读有关requestAnimationFrame的更多信息

浏览器支持(IE10+)

对于浏览器,我同意使用setTimeout和setInterval。

但是对于服务器端代码,它可能需要一个阻塞函数(例如,这样可以有效地进行线程同步)。

如果您正在使用Node.js和Meteor,您可能遇到了在光纤中使用setTimeout的限制。下面是服务器端睡眠的代码。

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.js光纤,睡眠

到目前为止,我读到的每一个解决方案都是“让我们睡觉,看看明天发生了什么”。

setInterval(callback,time)将等待很长时间,然后调用回调,同时阻塞运行时。“setInterval”的当前实现远非线程保存,甚至不考虑并发性。

虽然上面提到的稀疏解决方案看起来像,猜猜看,C#(笑),但它们仍然不像在C#/.NET中那样工作。

JavaScript目前没有提供实现真正多线程的架构。最好的方法是TypeScript,但这仍然缺乏太多真正的解决方案,以至于它……令人痛苦。JavaScript、jQuery、AJAX、jNode,甚至TypeScript都只是一群依赖于实现者心情好坏的人。事实句点