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


Улучшенный setTimeout Допустим, у нас есть функция foo, которую необходимо вызвать через 100 миллисекунд. Делается это очень просто. setTimeout(foo, 100); Лаконичность кода исчезает, если функцию foo необходимо вызвать с параметрами. Тогда в setTimeot придётся передавать функцию-обёртку, внутри которой уже вызывать foo. setTimeout(function() { foo(1, 2); }, 100); Всё становится ещё хуже, если функцию foo нужно вызвать в определённом контексте, например в this. В этом случае необходимо сначала сохранить контекст в локальной переменной, после чего вызвать функцию foo с помощью call или apply. (Если нужный контекст не this, то он и так в переменной.) var that = this; setTimeout(function() { foo.apply(that, [1, 2]); }, 100); Чтобы избежать таких многоэтажных конструкций, лучше обернуть получившийся код в отдельную функцию. /** * Вызывает функцию указанное количество миллисекунд в контексте ctx с аргументами args. * @param {Number} millis * @param {Object} ctx * @param {Array} args * @return {Number} Идентификатор таймаута. */ Function.prototype.defer = function(timeout, ctx, args) { var that = this; return setTimeout(function() { that.apply(ctx, args || []); }, timeout); }; Теперь отложенный вызов в нужном контексте с параметрами выглядит намного лаконичнее. foo.defer(100, this, [1, 2]); Заметьте, что defer возвращает идентификатор создаваемого таймера, так что его можно отменить в случае необходимости. Вызов функции с задержкой При создании, например, подсказок по мере ввода данных пользователем (а-ля поисковые подсказки) необходимо делать запрос на сервер для получения данных. Если делать такие запросы после каждого нажатия клавиши, то большинство запросов не то, что выполниться не успеют, они даже отправиться не успеют. Поэтому в этом случае алгоритм такой: как только пользователь не печатает в течение определённого времени — делаем запрос. Что делать, если в процессе запроса пользователь опять начал печатать, — вопрос реализации. Смешивать логику запроса и вывода подсказок с логикой задержек не стоит, лучше вынести последнюю в отдельную функцию. /** * Возвращает функцию, вызывающую исходную с задержкой delay в контексте ctx. * Если во время задержки функция была вызвана еще раз, то предыдующий вызов * отменяется, а таймер обновляется. Таким образом из нескольких вызовов, * совершающихся чаще, чем delay, реально будет вызван только последний. * @param {Number} delay * @param {Object} ctx * @return {Function} */ Function.prototype.debounce = function(delay, ctx) { var fn = this, timer; return function() { var args = arguments, that = this; clearTimeout(timer); timer = setTimeout(function() { fn.apply(ctx || that, args); }, delay); }; }; Теперь, если у нас есть функция suggest, запрашивающая и показывающая подсказки, то можно привязать её к нужному элементу следующим образом. document.getElementById('text').onkeypress = suggest.debounce(500);