Contents
Что такое дебаунс?
Debounce — это функция-оболочка, которая ограничивает количество выполнений переданной ей функции определенным периодом времени.
Удобное использование
Допустим, при вводе текста в инпут мы хотим отправить запрос на сервер выпадающего списка вариантов для введенного значения.
По умолчанию запрос будет уходить при наборе следующей буквы, но чтобы не перегружать сервер запросов и не повторять отрисовку страницы с данными того же значения, мы хотим завернуть запрос в дебаунс с некоторым ограничением по времени .
Таким образом, запрос не будет происходить при вводе каждой последующей буквы, а будет ограничен определенным периодом времени.
Как настроить дебаунс?
Чтобы правильно реализовать debounce, нам нужно, чтобы каждый следующий вызов целевой функции «знал» о своем предыдущем вызове, и на основе этих данных debounce решает, выполнять ли функцию или отложить ее.
Для debounce “эти данные” являются идентификатором тайм-аута.
Что с замыканиями?
И несмотря на то, что для последующего обращения к идентификатору таймаута при ограничении этого идентификатора внешним изменением следует использовать замыкание.
Что такое закрытие?
Замыкание — это функция, которая инкапсулирует и возвращает функцию вместе с ее окружением.
Проще говоря, мы можем сказать, что окружение — или лексическое окружение — это блок кода внутри фигурных скобок.
Переменные, объявленные в этом окружении (в частности, в замыкании), с одной стороны, будут недоступны извне этого окружения. Особенностью языка javascript является то, что только он и его дочерние среды имеют доступ к переменным среды, при условии, что доступ запрашивается после объявления.
Второе свойство заключается в том, что окружение функции не удаляется из памяти сразу после выполнения самой функции. Таким образом, переменные, объявленные в замыкании, могут быть «разделены» между различными вызовами функции, возвращаемой замыканием.
Теперь посмотрим, как это будет работать на практике.
Во-первых, давайте объявим функцию закрытия debounce, которая принимает функцию обратного вызова и ее предел времени выполнения. Он будет содержать переменную, в которой хранится идентификатор тайм-аута каждого последующего вызова:
export const debounce = (callback, interval) => {
let prevTimeoutId;
return (...args) => {
prevTimeoutId = setTimeout(() => {
callback(args);
}, limit);
}
}
Мы передаем аргументы возвращаемой функции обратному вызову. Обратите внимание на порядок, в котором аргументы передаются при закрытии: …args — это аргументы, которые функция получит при последнем (то есть втором) вызове — например, действие объекта события, если, скажем, дебаунсер передается как обработчик события.
Также логика такова: при следующем вызове функции нам нужно очистить предыдущий вызов из памяти — это можно сделать по id таймаута с помощью функции clearTimeout. Далее нам нужно объявить новый таймаут, сохранить его ID и вернуть новую функцию:
const debounce = (callback, interval = 0) => {
let prevTimeoutId;
return (...args) => {
clearTimeout(timeoutId);
prevTimeoutId = setTimeout(() => callback(...args), interval);
}
}
Теперь, если мы теперь хотим использовать наше подавление дребезга на входе, это будет выглядеть так:
document.querySelector('input').addEventListener(
'input',
debounce(ev => console.log(ev.target.value), 1000)
);
А при наборе символов во вводе на консоль будут выведены только значения, введенные с интервалом в одну секунду.
Добавим отзывчивости!
Если мы хотим использовать debounce в реагирующем компоненте, его необходимо преобразовать в пользовательский реагирующий хук, чтобы предыдущий идентификатор тайм-аута и возвращаемая функция не исчезли из памяти. Это делается с помощью хуков useRef и useCallback соответственно:
const useDebounce = (callback, interval = 0) => {
const prevTimeoutIdRef = React.useRef();
return React.useCallback(
(...args) => {
clearTimeout(prevTimeoutIdRef.current);
prevTimeoutIdRef.current = setTimeout(() => {
clearTimeout(prevTimeoutIdRef.current);
callback(...args);
}, interval);
},
[callback, interval]
);
};
И если мы планируем использовать его больше в useEffect, то, чтобы избежать ошибок, мы должны сначала инициализировать useDebounce с обратным вызовом и интервалом в переменной, затем вызвать эту переменную в useEffect и передать ей необходимые аргументы.
Вот и все.
Надеюсь, вам понравилось это увлекательное путешествие в мир анти-отскоков и замыканий.
Я рекомендую следующую статью о замыканиях:
https://learn.javascript.ru/close
СПАСИБО