28 мая 2009 г.

Микропаттерны оптимизации в JavaScript: декораторы функций debouncing и throttling

Декораторы функций позволяют добавить дополнительное поведение функции, не изменяя ее. Сигнатура оригинальной и декорированной функции полностью совпадают.

Debouncing

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

  1. Реальный вызов происходит только в случае, если с момента последней попытки прошло время, большее или равное задержке.
  2. Реальный вызов происходит сразу, а все остальные попытки вызова игнорируются, пока не пройдет время, большее или равное задержке, отсчитанной от времени последней попытки.

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

Например, у вас есть suggest. Посылать запросы к серверу на каждый keyup расточительно и не нужно. Можно декорировать обработчик, чтобы он срабатывал только после того, как пользователь перестал нажимать на клавиши, допустим, в течение 300 миллисекунд:

function onKeyUp() { ... };

$('input[name=suggest]').keyup($.debounce(onKeyUp, 300));

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

$('input').bind('keyup blur', $.debounce(process, 300));

Throttling

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

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

Например, на resize окна (или, допустим, на mousemove) у вас срабатывает какой-то тяжелый обработчик. Можно его «затормозить»:

$(window).resize($.throttle(doComplexСomputation, 300));

В итоге, функция будет выполняться не чаще, чем раз в 300 миллисекунд.

Мои реализации в виде jQuery-плагина можно скачать с code.google.

Ps. Навеяно книжкой Николаса Закаса «Professional JavaScript for Web Developers, 2nd Edition», хотя в ней он путает debounce и throttle, называя первое вторым. На ajaxian тоже поднималась эта тема.

20 мая 2009 г.

Загрузка по требованию и jQuery

Несмотря на то, что необходимо минимизировать количество http-запросов, иногда (или часто, в зависимости от задачи) бывает полезно загружать часть «тяжелого» функционала только тогда, когда он действительно понадобится на странице.

У jQuery есть механизм, позволяющий осуществить это — $.getScript, однако, он обладает рядом недостатков:

  • не запоминаются уже загруженные или загружаемые в данный момент скрипты, при повторном запросе опять идет их загрузка.
  • нельзя указать сразу несколько скриптов
  • выключен кэш (к каждому урлу насильно приписываются параметры типа ?_=1242843920520). Зачем это было так жестко сделано, для меня осталось загадкой.
  • у коллбэка нельзя задать контекст (это вообще болезнь коллбэков jQuery, хотя, как недавно рассказал Азат, разработчиками уже делаются шаги в этом направлении).

Я написал небольшой плагин, лишенный вышеперечисленных недостатков:

$.requireScript(url, callback, [context], [options])

Cкачать и попробовать можно с code.google.