在setTimeout(0)调用之前,图像不会呈现

问题描述 投票:-2回答:1

参考代码:

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放在队列中 - 我希望图像能够在等待之前呈现。如何强制图像渲染,然后强制用户等待?

javascript sleep event-loop
1个回答
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(code [,delay]);

注意:代码一种替代语法,允许您包含字符串而不是函数,该函数在计时器到期时编译和执行。建议不要使用此语法,原因与使用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>
© www.soinside.com 2019 - 2024. All rights reserved.