Throttle и debounce

В библиотеке Interact.js часто возникает необходимость оптимизировать обработку событий, связанных с движением элементов, масштабированием или перетаскиванием. Когда события происходят очень часто (например, mousemove или resize), прямое реагирование на каждое событие может привести к значительной нагрузке на браузер и снижению производительности. Именно здесь на помощь приходят техники throttle и debounce.


Throttle

Throttle ограничивает количество вызовов функции, позволяя ей выполняться не чаще, чем раз в определённый интервал времени. Это особенно полезно для событий, которые происходят с высокой частотой, таких как перетаскивание (dragmove) или масштабирование (resizemove).

Принцип работы

  • Функция вызывается не чаще одного раза за заданный период времени.
  • Любые вызовы в промежутке между разрешёнными вызовами игнорируются.
  • Подходит для плавного обновления UI при постоянных событиях.

Пример использования в Interact.js

interact('.draggable').draggable({
    listeners: {
        move: throttle(function (event) {
            const target = event.target;
            const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
            const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

            target.style.transform = `translate(${x}px, ${y}px)`;
            target.setAttribute('data-x', x);
            target.setAttribute('data-y', y);
        }, 100) // ограничение вызова функции до одного раза в 100 мс
    }
});

Ключевой момент: Время задержки в миллисекундах (100 в примере) определяет частоту обновлений, что позволяет контролировать производительность и отзывчивость интерфейса.


Debounce

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

Принцип работы

  • Функция выполняется только после того, как события прекратились на определённый период.
  • Любые новые события сбрасывают таймер, откладывая вызов.
  • Применяется для сохранения состояния после окончания перетаскивания, отправки данных на сервер, или обработки ввода.

Пример использования

function savePosition(event) {
    console.log('Сохраняем позицию элемента', event.target.id);
}

interact('.draggable').draggable({
    listeners: {
        end: debounce(savePosition, 300) // вызов после окончания действия с задержкой 300 мс
    }
});

Ключевой момент: debounce помогает избежать лишних вычислений или сетевых запросов при высокочастотных событиях.


Отличие throttle и debounce

Характеристика Throttle Debounce
Когда вызывается Периодически, с фиксированным интервалом Только после того, как события прекратились
Подходит для Частых обновлений UI, перетаскивания Завершение действия, отправка данных, поиск
Поведение при частых событиях Игнорирует промежуточные события Сбрасывает таймер и откладывает вызов

Использование в связке с Interact.js

Interact.js предоставляет мощные события для перетаскивания, масштабирования и жестов. С помощью throttle и debounce можно:

  1. Ограничить частоту обновления позиции элементов (dragmove) для плавности.
  2. Выполнять сетевые запросы или сохранение состояния только после завершения перетаскивания (dragend).
  3. Контролировать частоту обновления интерфейса при масштабировании (resizemove).

Пример масштабирования с throttle

interact('.resizable').resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    listeners: {
        move: throttle(function (event) {
            let { x, y } = event.target.dataset;
            x = (parseFloat(x) || 0);
            y = (parseFloat(y) || 0);

            Object.assign(event.target.style, {
                width: `${event.rect.width}px`,
                height: `${event.rect.height}px`,
                transform: `translate(${x}px, ${y}px)`
            });
        }, 50) // обновление не чаще чем раз в 50 мс
    }
});

Пример сохранения данных после завершения перетаскивания

interact('.draggable').draggable({
    listeners: {
        end: debounce(function (event) {
            fetch('/save-position', {
                method: 'POST',
                body: JSON.stringify({ id: event.target.id, x: event.target.dataset.x, y: event.target.dataset.y })
            });
        }, 200) // отправка данных через 200 мс после завершения перетаскивания
    }
});

Встроенные функции или внешние библиотеки

Interact.js не содержит собственных реализаций throttle и debounce, поэтому обычно используют:

  • Lodash: _.throttle, _.debounce
  • Underscore: _.throttle, _.debounce
  • Простые самописные функции на JavaScript

Простая реализация throttle

function throttle(fn, limit) {
    let lastCall = 0;
    return function(...args) {
        const now = Date.now();
        if (now - lastCall >= limit) {
            lastCall = now;
            fn.apply(this, args);
        }
    };
}

Простая реализация debounce

function debounce(fn, delay) {
    let timer;
    return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

Эффективное использование throttle и debounce в Interact.js позволяет контролировать производительность интерфейса, снижать нагрузку на браузер и обеспечивать плавность взаимодействия с элементами, одновременно избегая лишних операций при завершении действий.