简体   繁体   中英

Image won't render before setTimeout(0) call

Reference code:

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 is called from a click handler.

Desired function: the image is rendered, and the user is forced to wait 1 second before interacting with the page in any way.

Actual function : the user waits 1 second, and the image loads.

I thought setTimeout would put sleep on a queue - I was hoping that the image would be rendered before they were made to wait. How can I force the image to render, and then force the user to wait?

setTimeout() and setInterval() take a function reference or code as a string (but, don't do that) as their first argument. Your code:

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

passes an actual function invocation of sleep and that's why you get sleep first (it's being called immediately) and the image load second. The return value from the function invocation is what winds up getting used as the function reference, but sleep doesn't return a value, so undefined winds up being passed to the timer and so nothing happens when the timer expires. The line would need to be:

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

so that a function reference would correctly be the first argument and the call to sleep wouldn't happen immediately.

From the docs:

Syntax:

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

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

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

NOTE: code An alternative syntax that allows you to include a string instead of a function, which is compiled and executed when the timer expires. This syntax is not recommended for the same reasons that make using eval() a security risk.

Also, setting a timer delay of 0 will never happen. The JavaScript runtime is synchronous and will only run the callback function specified in the timer when it has nothing else to do. As a result, you can never really know for absolute certain what the delay will wind up being. Think of the delay as the minimum amount of time you can expect to wait for your function to run. Having said that, I read somewhere that there was an absolute minimum of 16ms due to the latency between the JavaScript runtime and the browser's WebAPI.


Now, you are going to need to be able to trap the moment that the image actually gets rendered and that can be accomplished with .requestAnimationFrame() .

Then, what you need to do is much simpler. You set your timer to start as soon as the image has finished loading and that is done by setting up a callback on the image's load event.

But, your code does nothing to prevent the user from interacting with the page, so you'll need to add a "mask" over the page that prevents interaction.

I've made the timer 3 seconds and given the mask a grey color in the snippet below to show the effect better.

 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM