在setTimeout(0)调用

时间:2018-03-02 19:40:36

标签: javascript sleep event-loop

参考代码:

function sleep( sleepDuration ){
    var now = new Date().getTime();
    while(new Date().getTime() < now + sleepDuration){ /* do nothing */ } 
}

function submit_answer(label) {
  let image = get_node('img.to_label')
  let size = Math.floor(Math.random() * 500)
  image.src = `http://via.placeholder.com/${size}x${size}`
  setTimeout(sleep.call(this, 1000), 0)
}
从点击处理程序调用

submit_answer

所需功能:呈现图像,并强制用户等待1秒钟,然后以任何方式与页面进行交互。

实际功能:用户等待1秒钟,然后加载图像。

我认为setTimeout会将sleep放在队列中 - 我希望图像在等待之前呈现。如何强制图像渲染,然后强制用户等待?

1 个答案:

答案 0 :(得分:1)

setTimeout() setInterval() 将功能引用或代码作为字符串(但是,不要这样做)作为他们的第一个论点。你的代码:

setTimeout(sleep.call(this, 1000), 0)

传递sleep的实际函数调用,这就是为什么你首先得到睡眠(它立即被调用)和图像加载第二次。函数调用的返回值最终被用作函数引用,但是sleep没有返回值,所以undefined最终被传递给定时器,所以没有任何反应计时器到期。该行必须是:

setTimeout(function(){ sleep.call(this, 1000) }, 0)

以便函数引用正确地成为第一个参数,并且不会立即调用sleep

来自文档:

  

<强>语法:

     

var timeoutID = scope.setTimeout( function [,delay,param1,param2,...]);

     

var timeoutID = scope.setTimeout( function [,delay]);

     

var timeoutID = scope.setTimeout(代码 [,延迟]);

     

注意:代码   一种替代语法,允许您包含字符串而不是函数,该函数在计时器到期时编译和执行。建议不要使用此语法,原因与使用eval()存在安全风险的原因相同。

此外,永远不会设置0的定时器延迟。 JavaScript运行时是同步的,只有在没有其他任何操作时才会运行计时器中指定的回调函数。结果,你永远无法真正知道延迟最终将会是什么。将延迟视为等待函数运行所需的最短时间。话虽如此,我在某处读到,由于JavaScript运行时和浏览器的WebAPI之间存在延迟,因此绝对最小值为16毫秒。

现在,您需要能够捕捉图像实际渲染的时刻,并且可以使用 .requestAnimationFrame() 来完成。

然后,你需要做的事情要简单得多。您将计时器设置为在图像加载完成后立即启动,并通过在图像的load事件上设置回调来完成。

但是,您的代码不会阻止用户与页面进行交互,因此您需要在页面上添加“掩码”以阻止交互。

我已经让计时器3秒,并在下面的片段中给出了一个灰色的面具,以更好地显示效果。

var mask = document.getElementById("mask");
    
function startRender() {
  // Rendering started, run callback when next render occurs
  requestAnimationFrame(rendered);  
}

function rendered() {
  sleep(3000);  // Render complete
}

// Nothing happens until the image fires off its load event...
document.querySelector("img").addEventListener("load", function(){
  // Run callback when next render occurs
  requestAnimationFrame(startRender);
});

function preventKeystrokes(evt){
  preventDefault();
}

function sleep(duration){
  mask.classList.remove("hidden");  // Show mask to prevent interactions
  window.addEventListener("keydown", preventKeystrokes); // prevent keystrokes
  // Count to three
  setTimeout(function(){
    mask.classList.add("hidden");  // Remove mask
    window.removeEventListener("keydown", preventKeystrokes); // Enable keyboard
  }, duration);
}
#mask { position:fixed; top:0; left:0; z-index:99; background-color:rgba(0,0,0,.6); width:100%; height:100%; }
.hidden { display:none; }
<button>Try to click me!</button>
<img src="http://imgsrc.hubblesite.org/hvi/uploads/image_file/image_attachment/30466/STSCI-H-p1801a-m-2000x1692.png" alt="big image">
<div id="mask" class="hidden"></div>