Объяснение Debounce - как заставить JavaScript ждать, пока пользователь перестанет вводить

Функция высшего порядка - это функция, которая принимает в качестве аргумента другую функцию, либо возвращает другую функцию в return. Наша debounce-функция делает и то, и другое.

Самый популярный способ использования debounce-функции - это добавление её в слушатель, прикрепленный к HTML-элементу. Чтобы лучше понять для чего это нужно и чем может быть полезным, давайте рассмотрим пример.

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

Вы хотите, чтобы функция выполнялась после того, как прошло хотя бы 2 секунды с момента ввода.

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

Теперь, каждый раз, когда вы жмёте клавишу, myFunc будет выполняться только если прошло 2 секунды с последнего момента вызова функции.

Как создать debounce-функцию?

Весь код займёт 7 строк. Оставшуюся часть статьи мы будем разбирать эти 7 строк, чтобы понять как это всё работает.

function debounce( callback, delay ) {
    let timeout;
    return function() {
        clearTimeout( timeout );
        timeout = setTimeout( callback, delay );
    }
}

Итак, на первой строке мы объявили функцию debounce. Эта функция принимает два аргумента - callback и delay.

function debounce( callback, delay ) {

}

callback - это любая функция, выполнение которой нужно ограничить.

delay - время в миллисекундах, которое указывает задержку.

function debounce( callback, delay ) {
    let timeout;
}

На второй строке мы объявляем переменную timeout, которая будет хранить timeoutID, возвращаемый при установке интервала через setTimeout.

function debounce( callback, delay ) {
    let timeout;
    return function() {
    }
}

На третьей строке мы возвращаем анонимную функцию. Эта функция, через замыкание, позволит нам получить доступ к переменной timeout после того, как выполнение функции debounce закончится.

Замыкание в JS происходит каждый раз, когда функция сохраняет доступ к лексической области внешней функции, даже если внешняя функция уже выполнилась. Если хотите узнать больше, можете почитать главу 7 книги «Вы не знаете JS»
function debounce( callback, delay ) {
    let timeout;
    return function() {
        clearTimeout( timeout );
    }
}

На четвёртой строке мы вызываем clearTimeout. Это гарантирует нам, что при каждом вызове debounce будет сбрасывать timeout и отсчёт будет начинаться с нуля.

function debounce( callback, delay ) {
    let timeout;
    return function() {
        clearTimeout( timeout );
        timeout = setTimeout( callback, delay );
    }
}

На пятой строке мы достигаем конца функции debounce.

Мы вызываем setTimeout, чтобы создать таймаут, с указанной в delay задержкой, который выполнит функцию callback, так же переданную аргументом. Вызов setTimeout вернёт нам timeoutID, который мы запишем в переменную timeout, чтобы мы могли сбросить таймаут и начать отсчёт с нуля, если функция debounce будет вызвана ещё раз.

Строки 6 и 7 содержат только закрывающие скобки, так что нет смысла их рассматривать.

Именно так работает debounce-функция изнутри. Теперь давайте добавим всё это к примеру из начала статьи. Мы создадим инпут и добавим слушатель с нашей debounce-функцией.

Пример из реального мира

Для начала, создадим инпут.

<label for="myInput">Type something in!</label>
<input id="myInput" type="text">

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

function helloWorld() {
    console.log("Hello World!")
}

И наконец мы мы должны добавить к нашему инпуту слушатель на событие keyup.

const myInput = document.getElementById("myInput");

myInput.addEventListener(
    "keyup",
    debounce( helloWorld, 2000 )
);

Теперь, при вводе данных в поле, функция helloWorld будет выполняться только если с момента завершения ввода прошло 2 секунды.

Особое спасибо пользователю Reddit с ником stratoscope за помощь с поправками в изначальном коде в статье.

Debounce-функция - это простая, но довольно мощная штука, которая может оказать большое влияние на JS-приложение. Многие компании используют её, чтобы улучшить производительность своих приложений.

Если вы хотите узнать больше о JS, заходите на сайт автора juanmvega.com.

Комментариев ещё нет. Оставьте первый!