Share a byte interview question: How to implement punctual setTimeout

c3e480c2fc6b64a66f874878afb670d3.jpeg

Recently, a classmate was asked this question during an interview. So we use this article to answer this question.

background

setTimeout isnot allowed. Because setTimeout is a macro task, its specified time refers to: The time to enter the main thread.

setTimeout(callback, 进入主线程的时间)

So when callback can be executed, it depends on how many tasks are left to be executed in front of the main thread.

Hence, this problem arises.

We can demonstrate this through this scenario:

082e3071d41f4c1b6b5af075025c042e.gif

The running code is as follows. A counter is used to record each call to setTimeout. The set interval * the number of counts is equal to the delay under ideal conditions. Use the following example to check the accuracy of our timer.

function timer() { 
   var speed = 50, // 设定间隔 
   counter = 1,  // 计数 
   start = new Date().getTime(); 
    
   function instance() 
   { 
    var ideal = (counter * speed), 
    real = (new Date().getTime() - start); 
     
    counter++; 
    form.ideal.value = ideal; // 记录理想值 
    form.real.value = real;   // 记录真实值 
 
    var diff = (real - ideal); 
    form.diff.value = diff;  // 差值 
 
    window.setTimeout(function() { instance(); }, speed); 
   }; 
    
   window.setTimeout(function() { instance(); }, speed); 
timer();

And if we add some additional code logic before setTimeout is executed, let's take a look at the difference.

... 
window.setTimeout(function() { instance(); }, speed); 
for(var x=1, i=0; i<10000000; i++) { x *= (i + 1); } 
...
f622a71e22f7c148144f1e15e2ea9668.gif

As can be seen, this greatly exacerbates the error.

It can be seen that as time goes by, the difference between the actual execution time of setTimeout and the ideal time will become larger and larger, which is not what we expected. Analogous to real scenes, some countdowns and animations will cause time deviations, which is not ideal.

This site diagram can describe the above issues very well:

f683408c705c0fe2a90c5aa1b651af39.png

How to implement punctual "setTimeout"

requestAnimationFrame

window.requestAnimationFrame() tells the browser that you want to perform an animation and requires the browser to call the specified callback function to update the animation before the next redraw.

This method needs to pass in a callback function as a parameter. The callback function will be executed before the next redraw of the browser. The number of executions of the callback function is usually 60 times per second, that is, once every 16.7ms, but it is not necessarily guaranteed. 16.7ms.

We can also try it to simulate setTimeout.

// 模拟代码 
function setTimeout2 (cb, delay) { 
    let startTime = Date.now() 
    loop() 
   
    function loop () { 
      const now = Date.now() 
      if (now - startTime >= delay) { 
        cb(); 
        return; 
      } 
      requestAnimationFrame(loop) 
    } 
}
364c848ae22bb37f0a360bb0590ee9b2.gif

It was found that due to the execution interval of 16.7 ms, using a timer with a small interval can easily lead to inaccurate time.

f65f35f19c2d779124cc28201e1ac552.png

Let’s look at the effect of introducing additional code.

... 
 window.setInterval2(function () { instance(); }, speed); 
for (var x = 1, i = 0; i < 10000000; i++) { x *= (i + 1); } 
...
e4f2ce35c6dcc1420b9b916c340527a7.gif

The increase in error is slightly exacerbated, so this solution is still not a good solution.

while

To get accurate, our first reaction is if we can actively trigger, get the initial time, and continuously poll the current time. If the difference is the expected time, then the timer must be accurate, then use while can achieve this function.

It’s also very simple to understand:

8deedbfeac973635df709e7a84715182.png

code show as below:

function timer(time) { 
    const startTime = Date.now(); 
    while(true) { 
        const now = Date.now(); 
        if(now - startTime >= time) { 
            console.log('误差', now - startTime - time); 
            return; 
        } 
    } 
timer(5000);

Print: Error 0

Obviously this method is very accurate, but we know that js runs in a single thread. Using this method to forcefully occupy the thread will cause the page to enter a stuck state. Such a result is obviously inappropriate.

setTimeout system time compensation

This solution is a solution I saw on stackoverflow. Let’s take a look at the difference between this solution and the original solution.

original plan

338d4ddf6e89c420cad3144559d8e862.png

setTimeout system time compensation

386f35024bed25e35a92893969a4eb7e.png

Each time the timer is executed, the system time is obtained for correction. Although there may be errors in each run, the system time is used to repair each run, so that every subsequent time can be compensated.

function timer() { 
   var speed = 500, 
   counter = 1,  
   start = new Date().getTime(); 
    
   function instance() 
   { 
    var real = (counter * speed), 
    ideal = (new Date().getTime() - start); 
     
    counter++; 
 
    var diff = (ideal - real); 
    form.diff.value = diff; 
 
    window.setTimeout(function() { instance(); }, (speed - diff)); // 通过系统时间进行修复 
 
   }; 
    
   window.setTimeout(function() { instance(); }, speed); 
}
3f7ba9eace6b3c0603914da05795bebd.png

Let's look at adding additional code logic.

a023c399a276ca31d3f4489cb91993c7.png

It is still very stable, so through the system's time compensation, we can make our setTimeout more punctual. At this point, we have completed the exploration of how to make setTimeout punctual.

Okay, let’s finally summarize the advantages and disadvantages of the four options.

while、Web Worker、requestAnimationFrame、setTimeoutSystem time compensation

b88f86db40ed071d311cb69f3ede93a3.png

Guess you like

Origin blog.csdn.net/Ed7zgeE9X/article/details/134778330