Anti-shake-debounce для оптимизации производительности интерфейса

задняя часть внешний интерфейс Promise

На этой неделе получил запрос - нечеткое сопоставление для полей ввода. Это не просто, просто прослушайте входное событие, получите входное значение и настройте интерфейс, верно? Тем не менее, бэк-энд сказал, что нет, объем данных этого интерфейса очень велик, частота вызовов интерфейса таким образом слишком высока, и нет необходимости вызывать интерфейс, когда пользователь вводит, до тех пор, пока интерфейс настраивается в тот момент, когда пользователь перестает вводить. Фу? Как эта сцена звучит как анти-шейк?

Так что же такое анти-шейк? Вы, должно быть, видели такие веб-сайты, которые размещают рекламные места посередине левой и правой сторон.Когда веб-страница прокручивается, чтобы рекламное место оставалось в середине экрана, необходимо постоянно вычислять положение. встряхнуть». Чтобы предотвратить это, он называется анти-встряска!

Каков принцип антивибрации? Мне всегда кажется, что пример, циркулирующий в Интернете, очень нагляден: когда мы поднимаемся на лифте, если в это время кто-то подойдет, мы будем продолжать нажимать на кнопку двери, чтобы подождать из вежливости. Приходится повторять предыдущую операцию еще раз, если пространство лифта бесконечно, то ждать придется вечность. . . Конечно, человеческое терпение ограничено! Итак, мы устанавливаем время, например 10 секунд, если никто не придет в течение 10 секунд, дверь лифта будет закрыта.

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

Самый простой вариант реализации кода:

function debounce(fn, delay) {
    let timer = null;

    return function() {
        const context = this;
        const args = arguments;

        if (timer) {
            clearTimeout(timer);
            timer = null;
        }

        timer = setTimeout(() => {
            fn.apply(context, args);
        }, delay);
    };
}

FN - это функция, которая будет дебранна, задержка - это заданная задержка, Deboutence возвращает анонимную функцию, чтобы сформировать закрытие, а внутренне поддерживает таймер частного переменного. То, что мы всегда срабатываем, это возвращенная анонимная функция. Таймер вернет значение идентификатора в таймер. Если анонимная функция снова запускается в интервале задержки, таймер будет очищен и перезапущен.

Конечно, упрощенная версия уж точно не может удовлетворить повседневные нужды, такие как те, которые может потребоваться выполнить сразу в первый раз, поэтому следует внести небольшие изменения:

function debounce(fn, delay, immediate) {
    let timer = null;

    return function() {
        const context = this;
        const args = arguments;

        timer && clearTimeout(timer);

        if(immediate) {
            const doNow = !timer;

            timer = setTimeout(() => {
                timer = null;
            }, delay);

            doNow && fn.apply(context, args);
        }
        else {
            timer = setTimeout(() => {
                fn.apply(context, args);
            }, delay);
        }
    };
}

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

const doNow = !timer;

timer = setTimeout(() => {
    timer = null;
}, delay);

doNow && fn.apply(context, args);

Значение переменной doNow равно !timer, и функция fn будет выполняться только тогда, когда !timer имеет значение true. Когда он выполняется в первый раз, начальное значение timer равно null, поэтому fn будет выполняться немедленно. Если это не первое выполнение, подождите время задержки, прежде чем снова запустить выполнение fn. Уведомление! Отличие от упрощенной версии в том, что упрощенная версия запускается несколько раз в течение определенного периода времени и выполняется в последний раз. Версия с немедленным выполнением не будет выполняться в последний раз, и ее необходимо запустить снова.

Функция защиты от сотрясений может иметь возвращаемое значение, и мы также должны быть совместимы:

function debounce(fn, delay, immediate) {
    let timer = null;

    return function() {
        const context = this;
        const args = arguments;
        let result = undefined;

        timer && clearTimeout(timer);

        if (immediate) {
            const doNow = !timer;

            timer = setTimeout(() => {
                timer = null;
            }, delay);
            
            if (doNow) {
                result = fn.apply(context, args);
            } 
        }
        else {
            timer = setTimeout(() => {
                fn.apply(context, args);
            }, delay);
        }

        return result;
    };
}

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

Учитывая асинхронию, мы также можем вернуть обещание:

function debounce(fn, delay, immediate) {
    let timer = null;

    return function() {
        const context = this;
        const args = arguments;

        return new Promise((resolve, reject) => {
            timer && clearTimeout(timer);

            if (immediate) {
                const doNow = !timer;

                timer = setTimeout(() => {
                    timer = null;
                }, delay);

                doNow && resolve(fn.apply(context, args));
            }
            else {
                timer = setTimeout(() => {
                    resolve(fn.apply(context, args));
                }, delay);
            }
        });
    };
}

Таким образом, пока функция fn выполняется, она должна иметь возможность получить возвращаемое значение! Это также окончательный вариант анти-встряски!

В следующий раз поговорим о антишейковом брате-Throttling-throttle для оптимизации производительности переднего плана.