Проводка

Действия с задержкой. Таймеры в JavaScript

Действия с задержкой. Таймеры в JavaScript

Вчера немного переделывал движок моего блога, хотел ускорить загрузку сайта. Начала конечно с HTML кода, удалив лишние запросы на .js и .css файлы, объединил и сжал. Так же, решил оптимизировать изображения на сайте. Сделал 2 варианта главного изображения записи. Один в формате .JPG с ухудшенным качеством на 70%, а второй вариант в исходном качестве. К моему большому удивлению, это хорошо повлияло на загрузку сайта. Ведь исходное изображение весом в 351 Кб , сжалось до 42 Кб в формат .JPG.

Вы можете заметить, что сайт частично работает в режиме AJAX, так же и главные изображения подгружаются после загрузке самой страницы (это можно заметить, когда размытие изображения исчезает). Не скажу что скорость сайта увеличилась в over100500 раз, но разница небольшая чувствуется.

В следующей статье, я расскажу как оптимизировал сайт, а так же про отличные утилиты для сжатия файлов (CSS,JS,JPG и т.п.)

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



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

И сейчас, я расскажу вам как сделать на JavaScript выполнение функции с ожиданием. В моем случае это будет с поле .

Прежде всего установим обработчик на наше поле. Можно установить для 3 событий: onKeyUp , onKeyDown , onKeyPress , onChange и еще для других событий .

Объяснение событий:
  • .onKeyUp() отпустил клавишу.
  • .onKeyDown() - происходит в том случае, когда пользователь удерживает клавишу.
  • .onKeyPress() - происходит в том случае, когда пользователь нажал или отпустил клавишу.
  • .onChange() - происходит в том случае, когда пользователь ввел значение в поле и потерял фокус.

На все события я не буду приводить пример, только onKeyUp . У jQuery есть метод .on() , который может принимать все эти события в одно значение.

$("input").on("keyup", function(){ var $this = $(this); var $delay = 500; clearTimeout($this.data("timer")); $this.data("timer", setTimeout(function(){ $this.removeData("timer"); $.get("/search", { q: $this.val() }, function(){ alert("Поиск выполнен!"); }); }, $delay)); });

  • $this - хранит ссылку на объект нашего
  • $delay - время ожидания в миллисекундах. В нашем случае это полсекунды.

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

Далее скрипт присваивает новый таймер и выполнит ваш код, который запустится после ввода значения. В нашем случае это GET запрос на сервер, после выполнения которого, появится окно с текстом Поиск выполнен! .

Живой пример можно увидеть на данном блоге, в блоке поиска или примером ниже.

Почти все реализации JavaScript имеют внутренний таймер-планировщик, который позволяет задавать вызов функции через заданный период времени.

В частности, эта возможность поддерживается в браузерах и в сервере Node.JS.

setTimeout

Синтаксис:

Var timerId = setTimeout(func / code, delay[, arg1, arg2...])

Параметры:

Func/code Функция или строка кода для исполнения. Строка поддерживается для совместимости, использовать её не рекомендуется. delay Задержка в миллисекундах, 1000 миллисекунд равны 1 секунде. arg1 , arg2 … Аргументы, которые нужно передать функции. Не поддерживаются в IE9-.

Исполнение функции произойдёт спустя время, указанное в параметре delay .

Например, следующий код вызовет func() через одну секунду:

Function func() { alert("Привет"); } setTimeout(func, 1000);

С передачей аргументов (не сработает в IE9-):

Function func(phrase, who) { alert(phrase + ", " + who); } setTimeout(func, 1000, "Привет", "Вася"); // Привет, Вася

Если первый аргумент является строкой, то интерпретатор создаёт анонимную функцию из этой строки.

То есть такая запись тоже сработает:

SetTimeout("alert("Привет")", 1000);

Вместо них используйте анонимные функции, вот так:

SetTimeout(function() { alert("Привет") }, 1000);

Отмена исполнения clearTimeout

Функция setTimeout возвращает числовой идентификатор таймера timerId , который можно использовать для отмены действия.

Синтаксис:

Var timerId = setTimeout(...); clearTimeout(timerId);

В следующем примере мы ставим таймаут, а затем удаляем (передумали). В результате ничего не происходит.

Var timerId = setTimeout(function() { alert(1) }, 1000); alert(timerId); // число - идентификатор таймера clearTimeout(timerId); alert(timerId); // всё ещё число, оно не обнуляется после отмены

Как видно из alert , в браузере идентификатор таймера является обычным числом. Другие JavaScript-окружения, например Node.JS, могут возвращать объект таймера, с дополнительными методами.

Такие разночтения вполне соответствуют стандарту просто потому, что в спецификации JavaScript про таймеры нет ни слова.

Таймеры – это надстройка над JavaScript, которая описана в секции Timers стандарта HTML5 для браузеров и в документации к Node.JS – для сервера.

setInterval

Метод setInterval имеет синтаксис, аналогичный setTimeout .

Var timerId = setInterval(func / code, delay[, arg1, arg2...])

Смысл аргументов – тот же самый. Но, в отличие от setTimeout , он запускает выполнение функции не один раз, а регулярно повторяет её через указанный интервал времени. Остановить исполнение можно вызовом clearInterval(timerId) .

Следующий пример при запуске станет выводить сообщение каждые две секунды, пока не пройдёт 5 секунд:

// начать повторы с интервалом 2 сек var timerId = setInterval(function() { alert("тик"); }, 2000); // через 5 сек остановить повторы setTimeout(function() { clearInterval(timerId); alert("стоп"); }, 5000);

Модальные окна замораживают время в Chrome/Opera/Safari

Что будет, если долго не жать OK на появившемся alert ? Это зависит от браузера.

В браузерах Chrome, Opera и Safari внутренний таймер «заморожен» во время показа alert/confirm/prompt . А вот в IE и Firefox внутренний таймер продолжит идти.

Поэтому, если закрыть alert после небольшой паузы, то в Firefox/IE следующий alert будет показан сразу же (время подошло), а в Chrome/Opera/Safari – только через 2 секунды после закрытия.

Рекурсивный setTimeout

Важная альтернатива setInterval – рекурсивный setTimeout:

/** вместо: var timerId = setInterval(function() { alert("тик"); }, 2000); */ var timerId = setTimeout(function tick() { alert("тик"); timerId = setTimeout(tick, 2000); }, 2000);

В коде выше следующее выполнение планируется сразу после окончания предыдущего.

Рекурсивный setTimeout – более гибкий метод тайминга, чем setInterval , так как время до следующего выполнения можно запланировать по-разному, в зависимости от результатов текущего.

Например, у нас есть сервис, который раз в 5 секунд опрашивает сервер на предмет новых данных. В случае, если сервер перегружен, можно увеличивать интервал опроса до 10, 20, 60 секунд… А потом вернуть обратно, когда всё нормализуется.

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

Рекурсивный setTimeout гарантирует паузу между вызовами, setInterval – нет.

Давайте сравним два кода. Первый использует setInterval:

Var i = 1; setInterval(function() { func(i); }, 100);

Второй использует рекурсивный setTimeout:

Var i = 1; setTimeout(function run() { func(i); setTimeout(run, 100); }, 100);

При setInterval внутренний таймер будет срабатывать чётко каждые 100 мс и вызывать func(i) :

Вы обратили внимание?…

Реальная пауза между вызовами func при setInterval меньше, чем указана в коде!

Это естественно, ведь время работы функции никак не учитывается, оно «съедает» часть интервала.

Возможно и такое что func оказалась сложнее, чем мы рассчитывали и выполнялась дольше, чем 100 мс.

В этом случае интерпретатор будет ждать, пока функция завершится, затем проверит таймер и, если время вызова setInterval уже подошло (или прошло), то следующий вызов произойдёт сразу же .

Если функция и выполняется дольше, чем пауза setInterval , то вызовы будут происходить вообще без перерыва.

Исключением является IE, в котором таймер «застывает» во время выполнения JavaScript.

А так будет выглядеть картинка с рекурсивным setTimeout:



При рекурсивном setTimeout задержка всегда фиксирована и равна 100 мс.

Это происходит потому, что каждый новый запуск планируется только после окончания текущего.

Управление памятью

Сборщик мусора в JavaScript не чистит функции, назначенные в таймерах, пока таймеры актуальны.

При передаче функции в setInterval/setTimeout создаётся внутренняя ссылка на неё, через которую браузер её будет запускать, и которая препятствует удалению из памяти, даже если функция анонимна.

// Функция будет жить в памяти, пока не сработал (или не был очищен) таймер setTimeout(function() {}, 100);

  • Для setTimeout – внутренняя ссылка исчезнет после исполнения функции.
  • Для setInterval – ссылка исчезнет при очистке таймера.

Так как функция также тянет за собой всё замыкание, то ставшие неактуальными, но не отменённые setInterval могут приводить к излишним тратам памяти.

Минимальная задержка таймера

У браузерного таймера есть минимальная возможная задержка. Она меняется от примерно нуля до 4 мс в современных браузерах. В более старых она может быть больше и достигать 15 мс.

По стандарту , минимальная задержка составляет 4 мс. Так что нет разницы между setTimeout(..,1) и setTimeout(..,4) .

Посмотреть минимальное разрешение «вживую» можно на следующем примере.

В примере ниже каждая полоска удлиняется вызовом setInterval с указанной на ней задержкой – от 0 мс (сверху) до 20 мс (внизу).

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

Важно:

В Internet Explorer, нулевая задержка setInterval(.., 0) не сработает. Это касается именно setInterval , т.е. setTimeout(.., 0) работает нормально.

Откуда взялись эти 4 мс?

Почему минимальная задержка – 4 мс, а не 1 мс? Зачем она вообще существует?

Это – «привет» от прошлого. Браузер Chrome как-то пытался убрать минимальную задержку в своих ранних версиях, но оказалось, что существуют сайты, которые используют setTimeout(..,0) рекурсивно, создавая тем самым «асинхронный цикл». И, если задержку совсем убрать, то будет 100% загрузка процессора, такой сайт «подвесит» браузер.

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

Реальная частота срабатывания

В ряде ситуаций таймер будет срабатывать реже, чем обычно. Задержка между вызовами setInterval(..., 4) может быть не 4 мс, а 30 мс или даже 1000 мс.

    Большинство браузеров (десктопных в первую очередь) продолжают выполнять setTimeout/setInterval , даже если вкладка неактивна.

    При этом ряд из них (Chrome, FF, IE10) снижают минимальную частоту таймера, до 1 раза в секунду. Получается, что в «фоновой» вкладке будет срабатывать таймер, но редко.

    При работе от батареи, в ноутбуке – браузеры тоже могут снижать частоту, чтобы реже выполнять код и экономить заряд батареи. Особенно этим известен IE. Снижение может достигать нескольких раз, в зависимости от настроек.

    При слишком большой загрузке процессора JavaScript может не успевать обрабатывать таймеры вовремя. При этом некоторые запуски setInterval будут пропущены.

Вывод: на частоту 4 мс стоит ориентироваться, но не стоит рассчитывать.

Посмотрим снижение частоты в действии на небольшом примере.

При клике на кнопку ниже запускается setInterval(..., 90) , который выводит список интервалов времени между 25 последними срабатываниями таймера. Запустите его. Перейдите на другую вкладку и вернитесь.

Запустить повтор с интервалом в 90 мс Остановить повтор

Если ваш браузер увеличивает таймаут при фоновом выполнении вкладки, то вы увидите увеличенные интервалы, помеченные красным .

Кроме того, вы заметите, что таймер не является идеально точным;)

Разбивка долгих скриптов

Нулевой или небольшой таймаут также используют, чтобы разорвать поток выполнения «тяжелых» скриптов.

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

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

Например, осуществляется анализ и подсветка первых 100 строк, затем через 20 мс – следующие 100 строк и так далее. При этом можно подстраиваться под CPU посетителя: замерять время на анализ 100 строк и, если процессор хороший, то в следующий раз обработать 200 строк, а если плохой – то 50. В итоге подсветка будет работать с адекватной быстротой и без тормозов на любых текстах и компьютерах.

Итого
  • Методы setInterval(func, delay) и setTimeout(func, delay) позволяют запускать func регулярно/один раз через delay миллисекунд.
  • Оба метода возвращают идентификатор таймера. Его используют для остановки выполнения вызовом clearInterval/clearTimeout .
  • В случаях, когда нужно гарантировать задержку между регулярными вызовами или гибко её менять, вместо setInterval используют рекурсивный setTimeout .
  • Минимальная задержка по стандарту составляет 4 мс. Браузеры соблюдают этот стандарт, но некоторые другие среды для выполнения JS, например Node.JS, могут предоставить и меньше задержки.
  • В реальности срабатывания таймера могут быть гораздо реже, чем назначено, например если процессор перегружен, вкладка находится в фоновом режиме, ноутбук работает от батареи или по какой-то иной причине.

Браузерных особенностей почти нет, разве что вызов setInterval(..., 0) с нулевой задержкой в IE недопустим, нужно указывать setInterval(..., 1) .

Задачи

важность: 5

Напишите функцию printNumbersInterval() , которая последовательно выводит в консоль числа от 1 до 20, с интервалом между числами 100 мс. То есть, весь вывод должен занимать 2000 мс, в течение которых каждые 100 мс в консоли появляется очередное число.

Нажмите на кнопку, открыв консоль, для демонстрации:

printNumbersInterval()

P.S. Функция должна использовать setInterval .

Решение

Function printNumbersInterval() { var i = 1; var timerId = setInterval(function() { console.log(i); if (i == 20) clearInterval(timerId); i++; }, 100); } // вызов printNumbersInterval();

важность: 5

Сделайте то же самое, что в задаче Вывод чисел каждые 100 мс , но с использованием рекурсивного setTimeout вместо setInterval .

Решение

Function printNumbersTimeout20_100() { var i = 1; var timerId = setTimeout(function go() { console.log(i); if (i < 20) setTimeout(go, 100); i++; }, 100); } // вызов printNumbersTimeout20_100();

важность: 5

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

Поэтому решаем обрабатывать не весь код сразу, что привело бы к зависанию скрипта, а разбить работу на части: подсвечивать по 20 строк раз в 10 мс.

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

Через setInterval , с остановкой по окончании работы:

Timer = setInterval(function() { if (есть еще что подсветить) highlight(); else clearInterval(timer); }, 10);

Через рекурсивный setTimeout:

SetTimeout(function go() { highlight(); if (есть еще что подсветить) setTimeout(go, 10); }, 10);

Какой из них стоит использовать? Почему?

Решение

Нужно выбрать вариант 2, который гарантирует браузеру свободное время между выполнениями highlight .

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

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

Сохраните эту страницу для тестирования приведенных ниже примеров.

Когда вы используете эффекты, jQuery создает для каждого выбранного элемента очередь функций, выполняющих анимацию. Каждая такая функция начинает выполняться лишь после того, как закончится выполнение предыдущей. Существует ряд методов, позволяющих получать информацию о состоянии очереди или управлять ею. Эти методы приведены в таблице ниже:

Можете сами создать очередь эффектов, объединив в обычной цепочке вызовов функции, создающие анимационные эффекты, как показано в примере ниже:

$(function() { $("form").css({"position": "fixed", "top": "70px", "z-index": "2"}); $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"}); var timespan = "slow"; cycleEffects(); function cycleEffects() { $("h1") .animate({left: "+=100"}, timespan) .animate({left: "-=100"}, timespan) .animate({height: 223,width: 700}, timespan) .animate({height: 30,width: 500}, timespan) .slideUp(timespan) .slideDown(timespan, cycleEffects); } }); Запустить пример

В этом сценарии с помощью обычной цепочки вызовов методов jQuery создается серия анимационных эффектов, применяемых к элементу h1. В последнем эффекте в качестве функции обратного вызова используется функция cycleEffects(), которая повторно запускает анимационный процесс с самого начала.

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

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

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

Отображение элементов из очереди эффектов

Для просмотра содержимого очереди эффектов можно использовать метод queue() . Правда, пользы от этого будет не очень много, поскольку очередь может содержать один из двух возможных типов объектов данных. Если эффект выполняется, то соответствующим ему элементом очереди является строковое значение inprogress. Если же эффект не выполняется, то элементом очереди является функция, которая будет вызываться.

Все эффекты jQuery выполняются функцией doAnimation() , и поэтому вам не удастся определить, какой именно будет следующая анимация. Тем не менее, приступая к работе с очередью, всегда полезно просмотреть ее содержимое. Соответствующий пример приведен ниже:

") .appendTo("body").css({ position: "fixed", "z-index": "2", "border-collapse": "collapse", top: 100 }); var timespan = "slow"; cycleEffects(); printQueue(); function cycleEffects() { $("h1") .animate({left: "+=100"}, timespan) .animate({left: "-=100"}, timespan) .animate({height: 223,width: 700}, timespan) .animate({height: 30,width: 500}, timespan) .slideUp(timespan) .slideDown(timespan, cycleEffects); } function printQueue() { var q = $("h1").queue(); var qtable = $("table"); qtable.html("Длина очереди:" + q.length + ""); for (var i = 0; i < q.length; i++) { var baseString = "" + i + ":"; if (q[i] == "inprogress") { $("table").append(baseString + "Выполняется"); } else if (q[i].name == "") { $("table").append(baseString + q[i] + ""); } else { $("table").append(baseString + q[i].name + ""); } } setTimeout(printQueue, 500); } }); Запустить пример

В этом примере элемент form нам не нужен, в связи с чем мы убираем его из DOM и заменяем простым элементом table, который будем использовать для отображения содержимого очереди. В код добавлена также функция printQueue(), которая периодически вызывает саму себя через определенные промежутки времени. Эта функции вызывает метод queue(), а также отображает в элементе table количество элементов в очереди и некоторую информацию о них. Как уже подчеркивалось, особой ценности эта информации не представляет, однако она позволяет получить некоторую общую картину происходящего.

При первом вызове функции cycleEffects() в очереди эффектов находится шесть элементов, первый из которых находится в состоянии выполнения. Другие элементы являются экземплярами функции doAnimation(). По завершении выполнения каждого эффекта jQuery удаляет соответствующий элемент из очереди. Как только заканчивается выполнение последнего из находящихся в очереди эффектов, происходит повторный вызов функции cycleEffects(), восстанавливающей первоначальное состояние очереди, в котором в ней содержится шесть элементов.

Остановка эффектов и очистка очереди

Для остановки выполнения текущей анимации можно использовать метод stop() . Можно передать этому методу два дополнительных аргумента, каждый из которых принимает логические значения. Если первый аргумент равен true, то все другие эффекты удаляются из очереди и не будут выполняться. Указание значения true для второго дополнительного аргумента приведет к тому, что для анимируемых CSS-свойств будут установлены их целевые значения.

По умолчанию оба аргумента имеют значения false, означающие, что из очереди удаляется лишь текущий эффект и что анимируемые свойства сохраняют значения, которые они имели на момент остановки выполнения эффекта. Если очередь не очищается, то jQuery переходит к следующему эффекту и выполняет его, как обычно.

Пример использования метода stop() приведен ниже:

$(function() { $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"}); $("form").remove(); $("

") .appendTo("body").css({ position: "fixed", "z-index": "2", "border-collapse": "collapse", top: 100 }); $("СтопСтарт") .appendTo($("").appendTo("body") .css({position: "fixed", "z-index": "2", "border-collapse": "collapse", top: 100, left:200 })).click(function(e) { $(this).text() == "Стоп" ? $("h1").stop(true, true) : cycleEffects(); }); var timespan = "slow"; ... }); Запустить пример

Для демонстрации того, как работает метод stop(), в документ добавлены две кнопки. После щелчка на кнопке "Стоп" вызывается метод stop(), которому передаются два аргумента true. Это приводит к очистке оставшейся части очереди эффектов и немедленной установке целевых значений для анимируемых свойств элемента. Поскольку при использовании метода stop() функции обратного вызова не вызываются, циклическое выполнение метода cycleEffects() прерывается, и анимация прекращается. После щелчка на кнопке "Пуск" вызывается метод cycleEffects(), что приводит к возобновлению анимации.

Щелчок на кнопке "Пуск" во время выполнения анимации не смутит jQuery. Это лишь приведет к тому, что эффекты, используемые методом cycleEffects(), добавятся в очередь. Использование функций обратного вызова означает, что длина очереди эффектов будет испытывать незначительные колебания, однако это не отразится на анимации, и она будет продолжаться, как обычно.

Вставка задержки в очередь эффектов

Метод delay() позволяет вставить паузу между двумя эффектами, находящимися в очереди. Этот метод принимает один аргумент, который позволяет задать длительность паузы в миллисекундах. Пример использования метода delay() представлен ниже:

$(function() { $("form").css({"position": "fixed", "top": "70px", "z-index": "2"}); $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"}); var timespan = "slow"; cycleEffects(); function cycleEffects() { $("h1") .animate({left: "+=100"}, timespan) .animate({left: "-=100"}, timespan) .delay(1000) .animate({height: 223,width: 700}, timespan) .animate({height: 30,width: 500}, timespan) .delay(1000) .slideUp(timespan) .slideDown(timespan, cycleEffects); } });

В этом сценарии в последовательность анимационных эффектов вставлены две паузы, длительность каждой из которых составляет одну секунду.

Вставка функций в очередь

Можно добавлять в очередь собственные функции с помощью метода queue(), и они будут выполняться точно так же, как и стандартные методы эффектов. Этой возможностью удобно пользоваться для запуска других анимационных процессов, корректного выхода из цепочки анимаций на основе значения внешней переменной и многих других целей. Соответствующий пример содержится ниже:

$(function() { $("form").css({"position": "fixed", "top": "70px", "z-index": "2"}); $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"}); var timespan = "slow"; cycleEffects(); function cycleEffects() { $("h1") .animate({left: "+=100"}, timespan) .animate({left: "-=100"}, timespan) .queue(function() { $("img").fadeTo(timespan, 0).fadeTo(timespan, 1); $(this).dequeue(); }) .animate({height: 223,width: 700}, timespan) .animate({height: 30,width: 500}, timespan) .slideUp(timespan) .slideDown(timespan, cycleEffects); } }); Запустить пример

Переменная this ссылается на объект jQuery, для которого был вызван этот метод. Эта возможность весьма полезна, поскольку в некоторый момент вы обязательно должны вызвать в своей функции метод dequeue() , чтобы сделать возможным выполнение следующего эффекта или функции в очереди. В этом примере метод queue() используется для добавления функции, которая выполняет анимацию прозрачности элемента с использованием метода fade().

Эффекты, включенные в пользовательскую функцию, добавляются в очереди эффектов для элементов img. Каждый элемент имеет собственную очередь, и можно управлять ими независимо одна от другой. Если хотите анимировать одновременно несколько свойств элемента, с очередью которого ведется работа, то следует использовать метод animate(). В противном случае эффекты будут лишь последовательно добавляться в очередь.

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

Function cycleEffects() { $("h1") .animate({left: "+=100"}, timespan) .animate({left: "-=100"}, timespan) .queue(function(nextFunction) { $("img").fadeTo(timespan, 0).fadeTo(timespan, 1); nextFunction(); }) .animate({height: 223,width: 700}, timespan) .animate({height: 30,width: 500}, timespan) .slideUp(timespan) .slideDown(timespan, cycleEffects); }

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

В JavaScript есть внутренний таймер, который предоставляет возможность запускать функции через определённое время. Для этого используются функции setTimeout() или setInterval() . Они не имеют собственного объекта, а являются методами объекта window .

setTimeout()

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

Основная форма:

SetTimeout(функция[, задержка, аргумент1, аргумент2, ...])

Альтернативная форма:

SetTimeout(код[, задержка]) функция Имя функции, которая должна быть запущена. Функцию можно задать непосредственно при вызове setTimeout() . код Строка кода, которая будет преобразована в анонимную функцию. задержка Необязательный параметр. Время в миллисекундах, через которое должна выполниться функция. По умолчанию устанавливается 0. аргумент1, аргумент2, ... Аргументы, которые будут переданы в функцию.

В примере, приведённом ниже, представлены два вызова метода setTimeout() , которые приведут к одному результату.

SetTimeout(function() { alert("Прошла 1 секунда"); }, 1000); // или setTimeout("alert("Прошла 1 секунда")", 1000);

Альтернативная форма не рекомендуется для использования. Для этого есть много причин, но основные - это потеря производительности и возможные проблемы при минимизации кода. Эта форма существует только для совместимости со старыми скриптами.

Метод setTimeout() возвращает идентификатор таймера. Он используется для отмены таймера.

Вызов setTimeout() устанавливает задержку только для переданной функции. Всё, что следует за этим методом, продолжает выполняться без паузы во времени.

Отмена setTimeout

Для отмены действия setTimeout() используется метод clearTimeout() .

ClearTimeout(идентификатор) идентификатор Идентификатор таймера, полученный при вызове setTimeout() .

Пример использования setTimeout BOM-интерфейс Заморозить на 3 секунды var button = document.body.firstElementChild; button.onclick = function() { button.disabled = true; setTimeout(function() { /* запуск таймера */ button.disabled = false; }, 3000); };

Заморозить на 3 секунды

setInterval()

Метод setInterval() используется для многократного запуска определённой функции через заданный промежуток времени. Синтаксис этого метода полностью аналогичен .

Основная форма:

SetInterval(функция[, задержка, аргумент1, аргумент2, ...])

Альтернативная форма:

SetInterval(код[, задержка]) функция Имя функции, которая будет повторяться. Функцию можно задать непосредственно при вызове setInterval() . код Строка кода, которая будет преобразована в анонимную функцию. задержка Необязательный параметр. Время в миллисекундах, задающее интервал между запусками функции. По умолчанию устанавливается 0. аргумент1, аргумент2, ... Аргументы, которые будут переданы в функцию при каждом запуске.

Ниже приведён пример, в котором оба вызова метода setInterval() приведёт к одному результату.

SetInterval(function() { alert("Прошла 1 секунда"); }, 1000); // или setInterval("alert("Прошла 1 секунда")", 1000);

Метод setInterval() возвращает идентификатор таймера. Он используется для отмены таймера.

Вызов setInterval() устанавливает интервал только для переданной функции. Весь код, который следует за этим методом, продолжает выполняться без паузы во времени.

Отмена setInterval

Метод setInterval() будет запускать переданную функцию до тех пор, пока не будет принудительно остановлен. Для этого используется метод clearInterval() .

ClearInterval(идентификатор) идентификатор Идентификатор таймера, полученный при вызове setInterval() .

Пример использования setInterval BOM-интерфейс Заморозить на 5 секунд var button = document.body.firstElementChild; button.onclick = function() { var time = 5; button.disabled = true; button.innerHTML = "Разморозится через " + time; var interval_id = setInterval(function() { /* запуск таймера */ time--; if (time != 0) { button.innerHTML = "Разморозится через " + time; } else { clearInterval(interval_id); /* остановка таймера */ button.disabled = false; button.innerHTML = "Заморозить на 5 секунд"; } }, 1000); };

Заморозить на 5 секунд

Особенности использования таймеров Минимальная задержка ограничена

Теоретически в таймерах можно указывать нулевую задержку времени. Однако, функция всё равно будет запускаться с некоторой задержкой. В действующем стандарте минимальная задержка установлена в 4 мс. В современных браузерах она составляет от 4 до 10 мс. Это сделано для того, чтобы вызов setInterval() с нулевой задержкой не привёл к перегрузке процессора или сервера.

Максимальная задержка ограничена

Большинство браузеров хранят время задержки таймера в виде 32-битного целого числа. Это означает, что при использовании значения больше, чем 2147483647 (примерно 25 дней), произойдёт переполнение памяти и таймер запустится без задержки.

Задержка имеет погрешность

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

Задержка может ограничиваться браузером

В неактивных закладках задержка может сильно отличаться от заданной. Многие браузеры в целях экономии ресурсов для неактивных закладок устанавливают минимальную задержку в 1000 мс. Это будет видно, если при запущенном примере, приведённом выше, на несколько секунд перейти в другую вкладку.

Задержка, установленная в таймере, задаёт интервал между запусками функции. Но заданная функция всегда выполняется в течение некоторого времени. Это время никак не влияет на заданный интервал, если оно меньше интервала. Выполнение функции заканчивается раньше, чем требуется её повторный запуск.

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

Всё вышесказанное подтверждается следующим примером. Интервал таймера задан 50 мс. Время выполнения функции можно варьировать от 0 до 200 мс. Если введённое значение меньше 50, тогда интервал таймера будет соответствовать заданному (с некоторой погрешностью, про которую писалось выше). А если больше 50, тогда интервал между запусками будет равен времени выполнения функции.