SetInterval is a macro task, as discussed in macro and micro tasks for JS event loops.

Use it a lot and you’ll find that it’s not always accurate, and in extreme cases it can have some puzzling problems.

Below we list one by one..

The time after the task was pushed into the queue was not accurate

Timer code:

setInterval(fn(), N);
Copy the code

Fn () will be pushed to the queue after N seconds.

So, when the setInterval is pushed into the queue, if there are many tasks in front of it or a task is waiting for a long time, such as a network request, the execution time of the timer may not be consistent with the scheduled execution time.

Such as:

let startTime = new Date().getTime();
let count = 0;
// Time-consuming task
setInterval(function() {
  let i = 0;
  while (i++ < 1000000000);
}, 0);
setInterval(function() {
  count++;
  console.log(
    "Time difference from the set interval:".new Date().getTime() - (startTime + count * 1000),
    "毫秒"
  );
}, 1000);
/ / output:
// The interval is 699 ms
// The interval is 771 milliseconds
// The interval is 887 ms
// The interval is 981 ms
// The interval is 1142 milliseconds
// The interval is 1822 milliseconds
// The interval is 1891 ms
// The time difference from the original setting is 2001 milliseconds
// The interval is 2748 milliseconds
// ...
Copy the code

As you can see, the time difference is getting larger and more inaccurate.

Function operation takes too long, resulting in inaccuracy

Consider the extreme case, if the code inside the timer requires a lot of computation (which takes a long time), or DOM manipulation. This can take a long time, and it is possible that the previous code will be added to the queue before it has finished executing. The timer will also become inaccurate, or even execute twice at the same time.

The most common thing that happens is when you need to use Ajax to poll the server for new data, someone is bound to use setInterval. However, no matter what the network condition is, it will send the request over and over again, and the final interval may be quite different from the original time.

// Do a network poll, query data every second.
let startTime = new Date().getTime();
let count = 0;

setInterval(() = > {
    let i = 0;
    while (i++ < 10000000); // Assumed network latency
    count++;
    console.log(
        "Time difference from the set interval:".new Date().getTime() - (startTime + count * 1000),
        "毫秒"
    );
}, 1000) output:// The interval is 567 milliseconds
// The time difference is 552 milliseconds
// The time difference is 563 milliseconds
// The interval is 554 milliseconds (2 times).
// The interval is 564 milliseconds
// The time difference from the original setting is 602 milliseconds
// The time difference is 573 milliseconds
// The interval is 633 milliseconds

Copy the code

SetInterval has different disadvantages from setTimeout

Again, the time interval specified by the timer indicates when the timer code is added to the message queue, not when the code is executed. So the actual time when the code is executed is not guaranteed, depending on when it is picked up by the main thread’s event loop and executed.

setInterval(function.N) // I.e. : every otherNThe secondfunctionThe event is pushed to the message queueCopy the code

As you can see in the figure above, setInterval adds an event to the queue every 100ms. 100ms later, add T1 timer code to queue, there are still tasks in the main thread, so wait, some event is finished to execute T1 timer code; After another 100ms, the T2 timer was added to the queue. The main thread was still executing T1 code, so it waited. After another 100ms, another timer code would theoretically be pushed into the queue, but since T2 is still in the queue, T3 will not be added (T3 will be skipped), resulting in the queue being skipped. Here we can see that the T2 code was executed immediately after the T1 timer had finished, so the effect of the timer was not achieved.

To sum up, setInterval has two drawbacks:

  • When using setInterval, certain intervals are skipped;
  • Multiple timers may run consecutively.

The task generated by each setTimeout is pushed directly into the task queue. SetInterval checks whether the last task is still in the queue before pushing it to the task queue. If so, it does not add it, and if not, it adds it.

Therefore, setTimeout is generally used to simulate setInterval to avoid the above shortcomings.

Here’s a classic example to illustrate the difference:

for (var i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
Copy the code

As any of you who have done this know, it is 5 5 outputs at one time. So the question is: is it to print a 5 every second? Or five fives immediately after one second? Because the for loop is done five times, setTimeout is added five times to the time loop, waiting one second for all to execute.

Why is it that five 5’s are printed after one second? Simply put, since for is the main thread code, setTimeout is executed first.

Of course, why the output is not 1 to 5 is a question of scope, which is not explained here.

SetTimeout simulation setInterval

To sum up, in some cases the setInterval disadvantage is obvious, and to address these disadvantages, use settTimeout() instead.

  • No new timer is inserted into the queue until the previous timer runs out (fix defect 1)
  • Ensure timer interval (solve shortcoming 2)

The concrete implementation is as follows:

1. Write an interval method

let timer = null
interval(func, wait){
    let interv = function(){
        func.call(null);
        timer=setTimeout(interv, wait);
    };
    timer= setTimeout(interv, wait);
 },
Copy the code

2. Use it as setInterval()

interval(function() {}, 20);
Copy the code

3. Stop the timer

if (timer) {
  window.clearSetTimeout(timer);
  timer = null;
}
Copy the code

reference

  • Why use setTimeout to simulate setInterval?
  • Use settTimeout() instead of setInterval()