In the project, setInterval was used to make a timer that increased by itself every second. Suddenly, one day, the strange phenomenon of time running backwards appeared. Then after a wave of analysis, the general idea is that setInterval will be slowed down when the page is inactive, instead of executing at the interval you set. Abs (currentTime – startTime), currentTime is normally greater than or equal to startTime, setInterval timer is set to increment currentTime per second. If setInterval is slowed down, currentTime is smaller than startTime, currentTime1s is increased, math. abs(currentTime-startTime) is decreased by 1s 1s.

In order to solve this problem, two solutions were proposed. Solution a: When the component is initially loaded, use this time to initialize currentTime, use currentTimeBackup to back up currentTime, and use localCurrentTime to record the localCurrentTime. Growth of currentTime after:

currentTime = currentTimeBackup + new Date().valueOf() - localCurrentTime
Copy the code

This is not affected by setInterval being slowed down.

Scheme 2: The assumption of scheme 2 is that since setInterval has problems, we want to use setTimeout to simulate setInterval. At that time, we did not know that setTimeout has the same problems, and then designed the following functions:

function setAccurateInterval(fn, interval) {
  let count = 0;
  const startTime = new Date().getTime();
  function fixed() {
    count++;
    let offset = new Date().getTime() - (startTime + count * interval);
    let nextTime = interval - offset;
    if (nextTime < 0) nextTime = 0;
    setTimeout(fixed, nextTime);
 
    fn();
  }
  setTimeout(fixed, interval);
}
Copy the code

This function causes another phenomenon. After the page is inactive for a period of time, it is activated again, and the time increases rapidly. At first, I thought that the setTimeout task was cached, and a large wave of data was queried, but it was not.

Conclusion: The setTimeout task is not cached. The browser optimization mechanism and the code itself are responsible for this

When the page is active, the data is as follows:



Offset is very small, nextTime is basically 1s, there is no problem.

Switch the page so that the page where the timer is located is inactive, and the data is as follows:



You can see that the nowTime is 1914ms longer than the previous one.This is because the main JS thread is performing other tasks, waiting for the macro task to take its turn[See this article for JS tasks and event loops]That’s how long it’s been), and count is only incremented by 1, leaving nextTime only 85ms.However, the browser has an optimization mechanism. If the tag is not currently active, the timer interval is 1000 milliseconds. So it’s still going to be 1s plus 1s on the surface.

But what I also found was,SetTimeout for inactive tabs slows down again after about 300 executions, and the interval becomes 60 seconds. (There is no supporting material for this on the Internet at present, if fortunately someone saw this record, and know the relevant materials, welcome to leave a message, thank you very much). This causes the offset to be very large.



NextTime is 0, and when the page is active again, setTimeout(()=>{… },0), resulting in the phenomenon of time rising rapidly. At this time, the execution time of the two scheduled tasks will be much shorter than 1s (the fastest execution time is 4ms), and the count will increase rapidly, return to the theoretical normal value, and then return to the original normal state.

So that’s a summary of setTimeout and serInterval in use.

Note the implementation principle of setTimeout: