Scenarios and implementation of anti-shake and throttling

Keywords: less

Anti-shake

Anti-shake is the conversion of consecutive triggers over a period of time into one trigger.

Use scenarios

Rather than retrieving data for each input, you can usually retrieve it after a period of time when user input stops, as shown in the following figure:

Implementation Code

  function debounce<Return>(
    fn: (...params: any[]) => Return, 
    wait: number, /** waiting time */
    immediate: boolean /** Do you want to execute it once now */
  ): Executor<Return> {
    const now: () => number = Date.now.bind(Date);
    let lastTime: number = 0;
    let timer: number = null;
    let params: IArguments = null;
    let _this: Function | null = null;

    function later(): void {
      const nowTime: number = now();

      if (nowTime - lastTime < wait) {
        // Continue to wait if you don't have enough time to wait
        const remainTime = wait - (nowTime - lastTime);

        timer = setTimeout(later, remainTime);
      } else {
        // Wait time is up to perform callback
        debouncer.result = fn.apply(_this, params);

        timer = null;
        _this = null;
        params = null;
      }
    }

    function execute(): (Return | null) {
      lastTime = now();
      _this = this;
      params = arguments;

      try {
        if (immediate && timer === null) {
          // Execute once now
          debouncer.result = fn.apply(_this, params);
        }

        return debouncer.result;
      } finally {
        if (timer === null) {
          // Join time queue, wait for execution
          timer = setTimeout(later, wait);
        }
      }
    }

    // Create Executor
    const debouncer: Executor<Return> = {
      execute,
      result: null,
    };

    return debouncer;
  };

The principle is simple, mainly to determine whether the wait time has arrived or not, and if not, to continue joining the task queue for execution.Usage method:

import utils from '../index';

const input = document.querySelector('input');
const executor = utils.fn.debounce(function(value) {
  console.log('fetch');
  
  return value;
}, 300);

let value = null;

input.addEventListener('input', function(e) {
  executor.execute(e.target.value);
  value = executor.result;
});

The reason for returning an executor is that this makes it easy to get the value returned on the last function execution.

throttle

Throttling, as its name implies, reduces the frequency of triggers over time.

Use scenarios

Some events can be triggered less frequently.For example, lazy loading listens for the position of the calculated scrollbar, but it doesn't have to trigger each slide to reduce the frequency of the calculation without wasting resources; it also has the magnifying effect of making a commodity preview without having to calculate the position every time the mouse moves.

Implementation Code

throttle: function <Return>(
    fn: (...params: any[]) => Return,
    wait: number,
    {
      isExecuteAtStart = true,
      isExecuteAtEnd = true,
    }: ThrottleOptions = {
      isExecuteAtStart: true,
      isExecuteAtEnd: true,
    }
  ): Executor<Return> {
    let timer: number = null;
    let _this: Function = null;
    let params: IArguments = null;

    function execute(): (Return | null) {
      _this = this;
      params = arguments;

      if (isExecuteAtStart && timer === null) {
        // If you need to start executing without a timer
        executor.result = fn.apply(_this, params);
        _this = null;
        params = null;
      }

      if (isExecuteAtEnd) {
        // If execution is required at the end
        if (timer === null) {
          timer = setTimeout(function () {
            executor.result = fn.apply(_this, params);
            _this = null;
            params = null;
            timer = null;
          }, wait);
        }
      }

      return executor.result;
    }

    const executor: Executor<Return> = {
      execute,
      result: null
    };

    return executor;
}

Last

The purpose of anti-shake and throttling is to reduce unnecessary calculations, not waste resources, and trigger calculations only when appropriate.The source code can be in This project As you can see from the fn module in, there are many other useful functions that you are welcome to use and learn.If my article is helpful to you, you can say yes. Thank you.

Posted by dunnsearch on Sun, 22 Sep 2019 19:03:59 -0700