Решение задач по JavaScript на bigfrontend (BFE): throttle и debounce

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В этой серии статей я хочу решать BFE задачи (https://bigfrontend.dev) и разбирать решения. BFE - это сайт с задачками, которые позволяют подготовиться к фронтовому интервью и прокачать свои знания в JavaScript.

Сегодня я хочу рассмотреть 2 задачи, это throttle и debounce.

1. Throttle

Теория:

Throttle позволяет вызывать нужную функцию не более одного раза за заданный период времени.

На практике:

Например нам нужно обрабатывать скролл страницы и выполнять какую-то функцию fn, которая работает с координатами скролла. Мы можем подписаться напрямую на событие onscroll, но вызываться он будет очень часто (зависит от того, как мы быстро будем прокручивать), но даже если это сделать быстро, событий будет больше, чем 1-2. Можно задекорировать вызываемую функцию fn в throttle и вызывать один раз в N мс.

Это будет работать так:

  1. При первом вызове onscroll декорированный вариант сразу передает вызов в fn. Пользователь сразу видит изменения.

  2. Если мы продолжаем прокручивать страницу, то в течение N мс ничего не происходит. События onscroll игнорируются.

  3. Когда скролл остановится, декорированная функция подождет, пока не пройдет N мс и затем вызывает fn с последними координатами. Тем самым обработает конечные координаты, где остановился скролл.

Реализация:

Разберем решение:

Функция throttle принимает 2 аргумента: функция, которую необходимо вызывать и время ожидания.

В строках 7 - 9, нам нужно сохранить наш таймер, который мы создаем для триггера вызовов функции с заданным интервалом, и нужно сохранять контекст вызова и аргументы, чтоб когда отработает таймер, мы могли вызвать функцию с корректным контекстом и параметрами.

В строке 11 возвращается функция, т.к нам необходимо сделать замыкание и задекорировать исходную функцию в throttle.

​​const throttled = throttle(func, 3)

И теперь остается обработать 2 варианта: первый вызов и последующие. Первый вызов должен отработать сразу же, не дожидаясь завершения таймера. Последующие должны "перезаписываться" и вызываться должен самый последний по истечению таймера.

Проверка на первый вызов это или нет осуществляется по запущенному таймеру, поэтому в строке 12 можно ее и увидеть, если это уже не первый вызов, мы просто сохраняем контекст и аргументы.

Если же это первый вызов, то сразу же выполняется функция (строка 16) и запускается таймер (строка 18) с указанным временным интервалом. Дополнительная проверка на аргументы внутри таймера, это для того, чтоб если у нас был всего один вызов (а мы первый выполнили сразу же), чтоб не вызвался он повторно, после истечения времени. Если же вызовы были, то когда время пройдет, функция будет вызвана с сохраненными контекстом и аргументами (строка 20), таймер очищен (строка 22), данные последнего вызова тоже очищены (строки 23, 24).

2. Debounce

Теория:

Debounce позволяет вызывать функцию fn не более одного раза в заданное количество миллисекунд.

На практике:

Нам нужно обрабатывать скролл страницы и выполнять какую-то функцию fn, которая работает с координатами скролла. Использование debounce  будет вызывать функцию fn через N мс, если после запуска таймера не тригерилось еще одно событие, которое отменяет предыдущее.

Это будет работать так:

  1. При первом вызове onscroll декорированный вариант запускает таймер с вызовом fn.

  2. Если мы продолжаем прокручивать страницу, то предыдущий таймер отменяется и запускается новый.

  3. Если в заданное время N мс не происходит событий onscroll, то вызывается функция fn с контекстом и аргументами последнего события.

Реализация:

Функция debounce принимает 2 аргумента: функция, которую необходимо вызывать и время ожидания.

В строках 8 - 10, нам нужно сохранить наш таймер, который мы создаем для триггера вызовов функции по истечении времени, и нужно сохранять контекст вызова и аргументы, чтоб когда отработает таймер, мы могли вызвать функцию с корректным контекстом и параметрами.

В строке 12 возвращается функция, т.к нам необходимо сделать замыкание и задекорировать исходную функцию в debounce.

​​const debounce = debounce(func, 3)

Функция, которая будет выполняться по истечению времени это строка 16. Мы вызываем исходную функцию fn с сохраненными контекстом и аргументами последнего вызова и обнуляем их.

Так как при каждом последующем вызове мы должны отменять таймер и запускать заново, то в строке 23 реализована отмена.

Строка 25 запускает таймер с вызовом нашей функции fnCall через заданное время wait.

Заключение

Как вы видите, нет ничего сложного в реализации данных методов. Надеюсь описанный алгоритм из жизни позволит лучше запомнить как работают throttle и debounce и в чем их отличие.

В следующей статье разберем варианты с follow up (реализацию throttle/debounce с leading и trailing опциями)

Источник: https://habr.com/ru/post/585750/


Интересные статьи

Интересные статьи

Когда семья Хейзинг приобрела систему периодической дистилляции и установила ее в необычном строении под названием "Бункер", который был встроен в склон холма на острове в вашингтонском Пьюджет-Саунд,...
Как бы круто это ни звучало - “Логдайвинг” -  на самом деле ковыряние логов может быть не самым интересным занятием, а на первых порах даже вызывать фрустрацию (когд...
Изначально эта статья задумывалась как небольшой бенчмарк для собственного использования, да и вообще статьёй быть не планировалась, однако, в процессе проведения замеров всплыли некоторые инте...
Привет, Хабр! Вне всяких сомнений многие из вас уже открыли велосезон (а кто-то его не закрывал) и это похвально. Мы тоже катаемся, а вместе с этим возобновляем наш блог, в котором в прошлом году...
Сегодня мы поговорим о перспективах становления Битрикс-разработчика и об этапах этого пути. Статья не претендует на абсолютную истину, но даёт жизненные ориентиры.