Как закодировать устранение дребезга и дросселирование в TypeScript

В некоторых руководствах по интерфейсу вы, возможно, слышали о дебаунсинге и троттлинге, так в каких случаях они используются? Зачем это использовать?

На самом деле, все они предназначены для предотвращения возможных проблем с производительностью при высокочастотных операциях.

Отказ будет выполняться только один раз в течение N секунд после инициирующего события, и если он будет запущен снова в течение N секунд, отсчет времени начнется заново.

Вот код JavaScript:

const debounce = (func, ms) => {
  let timer;
  return (...args) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      func(...args);
    }, ms);
  };
};

Здесь мы разумно используем замыкания, сохраняем возвращаемое значение setTimeout в замыкании, а затем возвращаем новую функцию. Когда эта новая функция вызывается часто, она проверяет, существует ли действительный timer, и если да, очищает последний таймер, а затем регенерирует таймер.

Обратите внимание, что функция, которую мы возвращаем, является стрелочной функцией, что означает, что ее this не изменяется, поэтому, когда мы вызываем func, она также вызывается напрямую. Если вы хотите изменить точку исходной функции this при вызове, вы можете сделать это:

const debounce = (func, ms) => {
  let timer;
  return function (...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, ...args);
    }, ms);
  };
};

Глядя на приведенный выше код, мы возвращаем традиционную функцию, что означает, что ее this определяется на основе того, как вы ее вызываете. Затем мы используем func.apply(this, ...args);, чтобы передать это this исходной функции. Но обратите внимание, что если ваша исходная функция является стрелочной, то это не работает.

Подводя итог, я бы рекомендовал использовать первый способ и использовать стрелочные функции для определения исходной функции. Таким образом, this указывает на все функции, и ваш код становится более кратким и понятным.

Вот TypeScript с небольшим добавлением магии:

const debounce = <F extends (...args: any[]) => void>(func: F, ms: number) => {
  let timer: ReturnType<typeof setTimeout>;
  return (...args: Parameters<F>) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      func(...args);
    }, ms);
  };
};

Далее поговорим о троттлинге, он будет выполняться только один раз в N секунд, и не повлияет на повторный запуск в течение N секунд. То есть просто разбавляет частоту исполнения.

const throttle = (func, ms) => {
  let timer;
  return (...args) => {
    if (timer) return;
    timer = setTimeout(() => {
      func(...args);
      timer = null;
    }, ms);
  };
};

Можно видеть, что разница между ним и debounce заключается в том, что функция завершится, если есть действительный таймер, а debounce должен очистить последний таймер.

Проблема this тоже такая же, поэтому я не буду повторяться здесь, но рекомендуется использовать стрелочные функции.

Вот TypeScript с небольшим добавлением магии:

const throttle = <F extends (...args: any[]) => void>(func: F, ms: number) => {
  let timer: ReturnType<typeof setTimeout> | null;
  return (...args: Parameters<F>) => {
    if (timer) return;
    timer = setTimeout(() => {
      func(...args);
      timer = null;
    }, ms);
  };
};

Наконец, давайте поговорим о том, в каких случаях вы можете их использовать. Отказ можно использовать в случае, когда редактор модифицируется в режиме реального времени, и синхронизация будет пересчитана, если произойдет модификация. Или при изменении размера окна браузера функция прослушивателя может устранить дребезг. Дросселирование можно использовать в событиях прослушивателя прокрутки браузера или событиях воспроизведения видео, например, рассчитывая каждые 1s.

Не являетесь участником Medium? Поддержите меня здесь, став одним из них.

Спасибо за прочтение!

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord.