In project development, timers are often used, such as the countdown of lottery activities, or the round-robin request interface, etc. SetInterval is usually used to implement the timing. However, this method has a disadvantage. Although it can be set to execute continuously after a period of time, it actually only puts the event into the message queue. The actual execution time is uncertain. It is possible that another timer task may come in before the last timer task is completed. Therefore, it is not guaranteed that the timer can be executed according to the set time

function runTimer() {(function inner() {
        let t = setTimeout(() = > {
            console.log('time count')
            clearTimeout(t);
            inner();
        }, 1000); }) (); }Copy the code

Output a time count every second. The setInterval effect is achieved by calling itself repeatedly with an instant-execute method, ensuring that the interval between each execution is consistent.

This approach is relatively simple. We may have more than one timer during the development of a project, and we want to handle all timers uniformly. In this case, we can encapsulate a class to manage these timers, which contains the following

  • timerList— An array of timers
  • addTimer– totimerListMethod to add a timer. The parameter is an object containing the name, callback method, and time
  • runTimerThe method to execute a timer with the name of the timer
  • clearTimerThe method to clear a timer with the name of the timer

The concrete implementation is as follows:

// timer.js

class timer {
  timerList = [];

  addTimer(name, callback, time = 1000) {
    this.timerList.push({
      name,
      callback,
      time
    });
    this.runTimer(name);
  }

  runTimer(name) {
    const _this = this;
    (function inner() {
      const task = _this.timerList.find((item) = > {
        return item.name === name;
      });
      if(! task)return;
      task.t = setTimeout(() = > {
        task.callback();
        clearTimeout(task.t); inner(); }, task.time); }) (); }clearTimer(name) {
    const taskIndex = this.timerList.findIndex((item) = > {
      return item.name === name;
    });
    if(taskIndex ! = = -1) {
      // The timer may have already been pushed when it is deleted, so it should be removed first to prevent the timer from being repeated when it is added
      clearTimeout(this.timerList[taskIndex].t);
      this.timerList.splice(taskIndex, 1); }}}export default new timer();

Copy the code

Here special instructions under each timer method is defined in the timer t properties, convenient clearTimer method to remove the timer, this is in order to handle errors generated in a particular scenario, such as when we perform a timer to determine whether the timer exists, if there is a will delete the timer, Here we use the name value to determine that if the timer is simply removed from the array, it may have entered setTimeout when it is deleted. If a timer with the same name is started at this time, two same timers will be executed at the same time, so we need to do this.

By encapsulating the timer in this way, we can reference it on any page

import timer from './timer.js';

timer.clearTimer('xxx');  // Clear the timer first
timer.addTimer('xxx'.() = > {
    console.log(123)},1000)
Copy the code

If you want each timer to be named uniquely, you can also use Symbol to define the name of the timer. For example, you can create a new file that holds the timer name

// timeSymbol.js

export const statTimer = Symbol("statTimer");
export const endTimer = Symbol("endTimer");
Copy the code

Then refer to it as follows

import timer from './timer.js';
import {statTimer} from './timeSymbol.js';

timer.clearTimer(statTimer);  // Clear the timer first
timer.addTimer(statTimer, () = > {
    console.log(123)},1000)
Copy the code

This ensures that all timers are unique and you don’t have to worry about errors caused by timers with the same name.