Ожидающая функция fnDelay

в 18:32, , рубрики: javascript, optimization, patterns, метки: , ,

Когда-то я написал очень простую функцию, которая казалась мне костылем в том уже не помню каком проекте. Но она хорошо выполняла свою обязанность. Тогда я называл ее ожидающей функцией, а JavaScript называл ее fnDelay. Дело в том, что в приложении может быть функционал, который срабатывает после изменения какого-нибудь состояния. Но это изменение происходит (или может гипотетически происходить) так часто, что функционал будет срабатывать очень много раз, хотя на самом деле нам необходимо выполнить его только для уже измененного состояния (или еще для нескольких промежуточных). Иначе может быть чувствительная проблема в производительности вашего приложения. Ну ладно, достаточно пустых слов, разберем конкретный пример.

У нас есть асинхронный поиск, в поле ввода по буквам вводится текст и автоматически обновляются результаты поиска. На первый взгляд — ничего сложного, повесим на input событие keyup, при срабатывании которого будет происходить новый поиск по новой фразе.

document.getElementById('search').addEventListener('keyup', function() {
    // Мы не будем описывать тело функции search, она реализует то, что было описано выше
    search();
});

Но за то время, пока выполняется запрос, пользователь может успеть набрать следующую букву, тогда результаты предыдущего запроса будут уже неактуальны, а новый запрос не выполнится пока не закончится предыдущий (тут я слукавил, XHR запрос можно и отменить, но это частный случай). Представьте, что пользователь вводит быстро словосочетание «Дешевые билеты в Камбоджу». В таком случае функция search вызовется 25 раз. Это излишне. С другой стороны, если пользователь вводит не так быстро, то хорошо бы показывать ему промежуточные варианты, и еще нежелательно иметь какие-то задержки перед показом результатов.

Для этого и была изначально придумана «Ожидающая функция». Она ждет, пока прекратится ее цикличный вызов, т.е. если пройдет заданное время после ее вызова, и эта функция не будет вызвана повторно, то она выполняется, иначе действие передается следующему вызову.

fnDelay = (function(){
    var timer = 0;
    return function(callback, ms){
        clearTimeout(timer);
        timer = setTimeout(callback, ms);
    };
})();

И сразу же покажу как ее использовать.

document.getElementById('search').addEventListener('keyup', function() {
    fnDelay(function() {
        search();
    }, 200);
});

Эффективность выполнения этой функции регулируется аргументом ms. Для каждого конкретного случая нужно искать свою золотую середину, чтобы результат был и отзывчивым и не накапливалась очередь невыполненных задач. Теперь если пользователь при вводе сделает паузу хотя бы в 200 мс, то он получит новые результаты. Тем самым мы значительно уменьшаем количество вызовов функции search.

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

Добавлю также ссылку на небольшой Фидл с примером.

Автор: plutov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js