Управляем скоростью вызова функций

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

Фреймворк Underscore предоставляет два вспомогательных метода для этих целей.


function debounce(func, wait, immediate) {
    var timeout;
    return function () {
        var context = this, args = arguments;
        var later = function () {
            timeout = null;
            if (!immediate) {
                func.apply(context, args);
            }
        };
        if (immediate && !timeout) {
            func.apply(context, args);
        }
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

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


$(window).on("resize", debounce(function () {
    console.log("resize event");
}, 1000));

Пока пользователь будет менять окно в размерах, никаких сообщений в консоли не будет. Оно появится через 1 секунду после того, как он остановится.


$(window).on("resize", debounce(function () {
    console.log("resize event");
}, 1000, true));

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


function throttle(func, wait) {
    var context, args, timeout, throttling, more, result;
    var whenDone = debounce(function () {
        more = throttling = false;
    }, wait);
    return function () {
        context = this;
        args = arguments;
        var later = function () {
            timeout = null;
            if (more) {
                func.apply(context, args);
            }
            whenDone();
        };
        if (!timeout) {
            timeout = setTimeout(later, wait);
        }
        if (throttling) {
            more = true;
        } else {
            result = func.apply(context, args);
        }
        whenDone();
        throttling = true;
        return result;
    };
}

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

$(window).on("resize", throttle(function () {
    console.log("resize event");
}, 1000));

При изменении размеров окна сообщение будет выводиться в консоль ровно 1 раз в секунду.