Timer

JavaScript provides the function of timing code execution, called timer (timer), which is mainly completed by the two functions setTimeout() and setInterval(). They add timed tasks to the task queue.

setTimeout() The

setTimeout function is used to specify how many milliseconds after a certain function or a certain piece of code will be executed. It returns an integer that represents the number of the timer, which can be used to cancel the timer in the future.

var timerId = setTimeout(func|code, delay);
```In the

above code, the `setTimeout` function accepts two parameters, and the first parameter `func|code` is the name of the function or a section that will be delayed Code, the second parameter `delay` is the number of milliseconds to delay execution.

```javascript
console.log(1);
setTimeout('console.log(2)',1000);
console.log(3);
// 1
// 3
// 2
```The

above code will output 1 first And 3, and then wait 1000 milliseconds before outputting 2. Note that `console.log(2)` must be in the form of a string as a parameter of `setTimeout`.

If it is a function to postpone the execution, the function name is directly used as the parameter of `setTimeout`.

```javascript
function f() {
  console.log(2);
}

setTimeout(f, 1000);

If the second parameter of setTimeout is omitted, it defaults to 0.

setTimeout(f);
// Same as
setTimeout(f, 0);

In addition to the first two parameters, setTimeout also allows more parameters. They will be passed into the postponed function (callback function) in turn.

setTimeout(function (a,b) {
  console.log(a + b);
}, 1000, 1, 1);
```In the

above code, `setTimeout` has 4 parameters. The last two parameters will be used as the parameters of the callback function when the callback function is executed after 1000 milliseconds.

One more thing to note, if the callback function is an object method, then `setTimeout` makes the `this` keyword inside the method point to the global environment, not the object where it was defined.

```javascript
var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(obj.y, 1000) // 1
```The

output of the above code is 1, not 2. Because when `obj.y` runs after 1000 milliseconds, what `this` points to is not `obj`, but the global environment.

To prevent this problem, one solution is to put `obj.y` into a function.

```javascript
var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(function () {
  obj.y();
}, 1000 );
// 2
```In the

above code, `obj.y` is placed in an anonymous function, which makes `obj.y` execute in the scope of `obj` instead of executing in the global scope, so Able to display the correct value.

Another solution is to use the `bind` method to bind the `obj.y` method to `obj`.

```javascript
var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(obj.y.bind(obj), 1000)
// 2

setInterval()

The usage of setInterval function is exactly the same as setTimeout, the only difference is that setInterval specifies a certain task every other period The time is executed once, that is, an unlimited number of timing executions.

var i = 1
var timer = setInterval(function() {
  console.log(2);
}, 1000)
```In the

above code, a 2 is output every 1000 milliseconds, and it will run indefinitely until Close the current window.

Like `setTimeout`, in addition to the first two parameters, the `setInterval` method can also accept more parameters, which will be passed into the callback function.

The following is an example of implementing web page animation through the `setInterval` method.

```javascript
var div = document.getElementById('someDiv');
var opacity = 1;
var fader = setInterval(function() {
  opacity -= 0.1;
  if (opacity >= 0) {
    div.style.opacity = opacity ;
  } else {
    clearInterval(fader);
  }
}, 100);
```The

above code sets the transparency of the `div` element every 100 milliseconds until it is completely transparent.

A common use of `setInterval` is to implement polling. The following is an example of polling whether the hash value of the URL has changed.

```javascript
var hash = window.location.hash;
var hashWatcher = setInterval(function() {
  if (window.location.hash != hash) {
    updatePage();
  }
}, 1000);

setInterval What is specified is the interval between "start execution", and does not consider the time consumed by each task execution itself. So in fact, the interval between two executions will be less than the specified time. For example, setInterval specifies to execute once every 100ms, and each execution takes 5ms, then 95ms after the first execution ends, the second execution will start. If a certain execution takes a very long time, such as 105 milliseconds, then after it ends, the next execution will start immediately.

In order to ensure that there is a fixed interval between two executions, you can use setInterval instead of setInterval, but after each execution, use setTimeout to specify the specific time for the next execution.

var i = 1;
var timer = setTimeout(function f() {
  // ...
  timer = setTimeout(f, 2000);
}, 2000);
```The

above code can ensure that the next execution is always 2000 after the end of this execution Start in milliseconds.

## clearTimeout(), clearInterval()

`setTimeout` and `setInterval` functions all return an integer value, which represents the counter number. Pass the integer into the `clearTimeout` and `clearInterval` functions to cancel the corresponding timer.

```javascript
var id1 = setTimeout(f, 1000);
var id2 = setInterval(f, 1000);

clearTimeout(id1);
clearInterval(id2);
```In the

above code, the callback function `f` will not be executed Because both timers have been cancelled.

The integer values ​​returned by `setTimeout` and `setInterval` are continuous, that is, the integer value returned by the second `setTimeout` method will be 1 greater than the integer value of the first one.

```javascript
function f() {)
setTimeout(f, 1000) // 10
setTimeout(f, 1000) // 11
setTimeout(f, 1000) // 12
```In the

above code, when `setTimeout` is called three times in a row, the return value is 1 greater than the previous time.

Using this, you can write a function to cancel all the current `setTimeout` timers.

```javascript
(function() {
  // Check once per round of event loop
  var gid = setInterval(clearAllTimeouts, 0);

  function clearAllTimeouts() {
    var id = setTimeout(function() {}, 0);
    while (id> 0) {
      if (id !== gid) {
        clearTimeout(id);
      }
      id--;
    }
  }
})();
```In the

above code, first call `setTimeout` to get a calculator number, and then put All counters with numbers smaller than it are cancelled.

## Example: debounce function

Sometimes, we don't want the callback function to be called frequently. For example, the user fills in the content of the webpage input box, and hopes to send it back to the server through the Ajax method. jQuery is written as follows.

```javascript
$('textarea').on('keydown', ajaxAction);

This writing has a big disadvantage, that is, if the user continuously presses the keys, the keydown event will be triggered continuously, causing a lot of Ajax communication. This is unnecessary and is likely to cause performance problems. The correct approach should be to set a threshold value that represents the minimum time between two Ajax communications. If a new keydown event occurs within the interval, the Ajax communication will not be triggered and the timing will restart. If the specified time has passed and no new keydown event occurs, the data will be sent out again.

This approach is called debounce (anti-shake). Assuming that the interval between two Ajax communications should not be less than 2500 milliseconds, the above code can be rewritten as follows.

$('textarea').on('keydown', debounce(ajaxAction, 2500));

function debounce(fn, delay){
  var timer = null; // declare timer
  return function() {
    var context = this;
    var args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(context, args);
    }, delay);
  };

In the above code, as long as the user presses the key again within 2500 milliseconds, the last timer will be cancelled, and then a new timer will be created. This ensures that the call interval between callback functions is at least 2500 milliseconds.

## Operating mechanism The operating mechanism of

`setTimeout` and `setInterval` is to move the specified code out of this round of event loop, wait until the next round of event loop, and then check whether the specified time is up. If it arrives, execute the corresponding code; if it does not, continue to wait.

This means that the callback functions specified by `setTimeout` and `setInterval` must wait until all the synchronization tasks of this round of event loop have been executed before they start to execute. Since it is uncertain how long the previous task will take to complete, there is no way to guarantee that the tasks specified by `setTimeout` and `setInterval` will be executed at the scheduled time.

```javascript
setTimeout(someTask, 100);
veryLongTask();

The setTimeout of the above code specifies to run a task after 100 milliseconds. However, if the following veryLongTask function (synchronous task) runs for a very long time and cannot be completed after 100 milliseconds, then the delayed someTask can only wait and wait until the end of the veryLongTask run before it’s its turn carried out.

Let's look at another example of setInterval.

setInterval(function () {
  console.log(2);
}, 1000);

sleep(3000);

function sleep(ms) {
  var start = Date.now();
  while ((Date.now()-start) <ms) {
  }
}
```In the

above code, `setInterval` requires that a 2 be output every 1000 milliseconds. However, the following `sleep` statement takes 3000 milliseconds to complete, so `setInterval` must be postponed to 3000 milliseconds before it will take effect. Note that after it takes effect, `setInterval` will not produce a cumulative effect, that is, it will not output three 2s at once, but only one 2 will be output.

## setTimeout(f, 0)

### Meaning

The function of `setTimeout` is to postpone the execution of the code until the specified time. If the specified time is `0`, that is, `setTimeout(f, 0)`, will it be executed immediately?

The answer is no. As mentioned in the previous section, the callback function `f` specified by `setTimeout` must be executed after the synchronization tasks of the current script are all processed. In other words, `setTimeout(f, 0)` will be executed at the beginning of the next round of event loop.

```javascript
setTimeout(function () {
  console.log(1);
}, 0);
console.log(2);
// 2
// 1
```The

above code first outputs `2`, then `1 `. Because `2` is a synchronous task, executed in the current round of event loop, and `1` is executed in the next round of event loop.

In short, the purpose of `setTimeout(f, 0)` is to execute `f` as early as possible, but it does not guarantee that `f` will be executed immediately.

In fact, `setTimeout(f, 0)` will not really run after 0 milliseconds. Different browsers have different implementations. Take the Edge browser as an example, it will run after 4 milliseconds. If the computer is running on battery power, it will wait until 16 milliseconds to run; if the web page is not on the current Tab page, it will be delayed to 1000 milliseconds (1 second). This is to save system resources.

### The application of

`setTimeout(f, 0)` has several very important uses. One of its major applications is to adjust the sequence of events. For example, in web page development, an event occurs first in the child element and then bubbled to the parent element, that is, the event callback function of the child element will be triggered earlier than the event callback function of the parent element. If you want the event callback function of the parent element to happen first, you need to use `setTimeout(f, 0)`.

```javascript
// The HTML code is as follows
// <input type="button" id="myButton" value="click">

var input = document.getElementById('myButton');

input.onclick = function A() {
  setTimeout(function B() {
    input.value +=' input';
  }, 0)
};

document.body.onclick = function C() {
  input.
};
```The

above code first triggers the callback function `A` after clicking the button, and then triggers the function `C`. In function `A`, `setTimeout` defers function `B` to the next round of event loop execution, which serves the purpose of triggering the callback function `C` of the parent element first.

Another application is a user-defined callback function, which is usually triggered before the default action of the browser. For example, if the user enters text in the input box, the `keypress` event will be triggered before the browser receives the text. Therefore, the following callback function does not achieve its purpose.

```javascript
// The HTML code is as follows
// <input type="text" id="input-box">

document.getElementById('input-box').onkeypress = function (event) {
  this.value = this. value.toUpperCase();
}
```The

above code wants to convert the characters to uppercase immediately after the user enters text each time. But in fact, it can only convert the character before this input to uppercase, because the browser has not received the new text at this time, so `this.value` cannot get the latest input character. Only by rewriting with `setTimeout` can the above code work.

```javascript
document.getElementById('input-box').onkeypress = function() {
  var self = this;
  setTimeout(function() {
    self.value = self.value.toUpperCase();
  }, 0);
}
```The

above code puts the code in `setTimeout` so that it will be triggered after the browser receives the text.

Since `setTimeout(f, 0)` actually means that the task is executed in the earliest available idle period of the browser, so those tasks that are computationally intensive and time-consuming are often placed in several small parts, respectively Put it in `setTimeout(f, 0)` to execute.

```javascript
var div = document.getElementsByTagName('div')[0];

// Writing method one
for (var i = 0xA00000; i <0xFFFFFF; i++) {
  div.style.backgroundColor ='#' + i.toString (16);
}

// writing method two
var timer;
var i=0x100000;

function func() {
  timer = setTimeout(func, 0);
  div.style.backgroundColor ='#' + i.toString(16);
  if ( i++ == 0xFFFFFF) clearTimeout(timer);
}

timer = setTimeout(func, 0);

There are two ways to write the above code, both are to change the background color of a web page element. The first way of writing will cause the browser to "clog" because the execution speed of JavaScript is much higher than that of the DOM, which will cause a large number of DOM operations to "stack", while the second way of writing will not. This is the benefit of setTimeout(f, 0).

Another example of using this technique is the processing of code highlighting. If the code block is large and one-time processing may cause a lot of pressure on performance, then divide it into small blocks and process one block at a time, for example, write it as setTimeout(highlightNext, 50), performance pressure will be Lighten up.