Javascript 图像赢得';在setTimeout(0)调用之前不渲染

Javascript 图像赢得';在setTimeout(0)调用之前不渲染,javascript,sleep,event-loop,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

参考代码:

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)
}
功能睡眠(睡眠持续时间){
var now=new Date().getTime();
while(new Date().getTime()
submit\u-answer
从单击处理程序调用

所需功能:图像被渲染,用户在以任何方式与页面交互之前被迫等待1秒

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

我以为
setTimeout
会把
sleep
放在一个队列上-我希望图像会在他们等待之前呈现出来。如何强制图像渲染,然后强制用户等待?

将函数引用或将编码为字符串(但不要这样做)作为其第一个参数。您的代码:

setTimeout(sleep.call(this, 1000), 0)
传递一个实际的函数调用
sleep
,这就是为什么首先得到sleep(立即调用它),然后加载映像。函数调用的返回值最终被用作函数引用,但
sleep
不返回值,因此
undefined
最终被传递给计时器,因此计时器过期时不会发生任何事情。这条线需要是:

setTimeout(function(){ sleep.call(this, 1000) }, 0)
因此,函数引用将正确地作为第一个参数,并且不会立即调用
sleep

从文档中:

语法:

var timeoutID=scope.setTimeout(函数[,delay,param1,param2,…])

var timeoutID=scope.setTimeout(函数[,延迟])

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

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

此外,将计时器延迟设置为
0
也不会发生。JavaScript运行时是同步的,只有在没有其他事情可做时才会运行计时器中指定的回调函数。因此,你永远无法确切地知道延迟最终会是什么。将延迟视为等待函数运行的最短时间。话虽如此,我在某个地方读到,由于JavaScript运行时和浏览器WebAPI之间的延迟,绝对最小值为16ms


现在,您需要能够捕捉图像实际渲染的时刻,这可以通过实现

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

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

我把计时器设为3秒,并在下面的代码片段中给了面具一个灰色,以更好地显示效果

var mask=document.getElementById(“mask”);
函数startRender(){
//渲染已开始,下次渲染时运行回调
requestAnimationFrame(渲染);
}
函数呈现(){
睡眠(3000);//渲染完成
}
//在映像触发其加载事件之前,不会发生任何操作。。。
document.querySelector(“img”).addEventListener(“加载”,函数(){
//下次渲染时运行回调
requestAnimationFrame(startRender);
});
功能防止击键(evt){
预防默认值();
}
功能睡眠(持续时间){
mask.classList.remove(“hidden”);//显示掩码以防止交互
addEventListener(“keydown”,preventKeystrokes);//防止击键
//数到三
setTimeout(函数(){
mask.classList.add(“hidden”);//删除掩码
removeEventListener(“keydown”,防止击键);//启用键盘
},持续时间);
}
#遮罩{位置:固定;顶部:0;左侧:0;z索引:99;背景色:rgba(0,0,0,6);宽度:100%;高度:100%;}
.hidden{display:none;}
试着点击我!

1
setTimeout(sleep.call(this,1000),0)
可能不会按您的想法执行。2.不要使用那个愚蠢的睡眠功能,这是对用户CPU的浪费。你会使用onload事件,但使用那个睡眠是个坏主意。。。。它更好的名字应该是“冻结”或“锁定”,因为这就是它的全部功能。从UI的角度来看,睡眠功能正是我想要的。并不是每个应用程序都适合同一个模式——不过你是对的,总的来说,这是一个糟糕的想法@亚历山大·奥马拉请在未来对他人更加友善。你可以解释而不称之为“愚蠢”。你想让UI锁定吗?你也希望你的用户恨你吗?锁定浏览器是实现这一点的一个好方法。不,真的,没有理由这么做,你做错了什么。哎呀,我想我是在想“绑定”,而不是“呼叫”。谢谢你!但是,添加加载事件会导致相同的行为。尝试更改img src-事件处理程序在呈现图像之前运行。此外,这允许键盘交互。我正试图完全冻结窗口。@ChrisAnderson-Answer更新以解决这个问题(尽管这不是你的问题)。@ChrisAnderson-Answer再次更新。渲染完成后,可以使用
.requestAnimationFrame()
捕捉。请参阅更新的代码。这是我问题的一部分:“在与用户交互之前,用户被迫等待1秒