preface

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.

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

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.

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)
// 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

Ps: When setInterval is used, it is added to the task queue only when there is no instance of the timer code in the queue.

  • 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

In summary, in some cases, setInterval disadvantages are obvious, and to solve these disadvantages, setTimeout() can be used instead.

  • No new timer is inserted into the queue until the previous timer runs out (fix defect 1)
  • Ensure timer interval (solve shortcoming 2)
let timer = null
function myInterval(func, wait){
  let interv = function(){
    func.call(null)
    timer=setTimeout(interv, wait)
  }
  timer = setTimeout(interv, wait)
}
Copy the code

Call:

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

eliminate

if(timer){
  clearTimeout(timer)
  timer = null
}
Copy the code