Практика

JavaScript

и PHP

Вы думаете, setTimeout останавливает работу Javascript? Нет, таймеры в Javascript работают по-другому: они откладывают на определённое время только одну задачу.

учебник HTML скачать
(вся книга в одном файле 3.92М)

Статьи

Добавить статью:

*Заголовок:
Имя файла (только латинские буквы):
Автор:
*Текст:

Правила оформления:

  • HTML-тэги не работают (выводятся на экран в виде кода)
  • Абзац - два раза Enter (абзацы отделяются пустыми строками)
  • __курсив для группы__, **жирный для группы** (2 знака подчёркивания или 2 звёздочки перед первым словом группы и после последнего);

~~~~~~~~~~~~~~~~~~~~

Я построил дом: охрана дома. Охрана объектов любой сложности.


ir2.ru

ООО РИА «Лимон и К»
2006-2012

Иркутск-81, Депутатская, 79-27. Тел.: +7 3952 230413

~~~~~~~~~~~~~~~~~~~~

Таймеры в Javascript (setInterval, setTimeout)

В программировании на скриптовых языках периодически возникает необходимость создать паузу – приостановить выполнение программы на некоторое время, а потом продолжить работу. Например, в сценариях VBS и PHP возможны такие методы:

VBS: wscript.sleep 1500 (остановка на 1.5 секунды)

PHP: sleep(10); (остановка на 10 секунд)

Во время подобных пауз исполняющая система (PHP или VBS) ничего не делает. Разработчик, попытавшись интуитивно использовать нечто подобное в Javascript, будет неприятно удивлён. Типичная ошибка при попытке создать паузу в Javascript выглядит так:

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

  1. i = 1;
  2. откладываем добавление цифры "1" к полю ввода на 0.9 секунды;
  3. немедленно за постановкой этой задачи цикл идёт дальше: i=2;
  4. откладываем добавление цифры "2" к полю ввода на 0.9 секунды;
  5. ...

Немедленно означает, например, 1 мс (то есть несоизмеримо мало, по сравнению с 900 мс): цикл прозведёт свою работу практически мгновенно, создав несколько отложенных задач от одной и той же точки времени. Это значит, все отложенные задачи по "рисованию" будут выполнены практически в одно и то же время, без пауз между добавлением новых цифр. Цикл запускается; всё замирает на 0.9 с; и ширрр – все цифры выстреливаются в ряд одна за другой.

А как в подобном случае правильно применить setTimeout? Это сложно. Придётся вызывать функцию рекурсивно (изнутри функции ту же самую функцию), а чтобы этот процесс не был бесконечным, задать условие остановки (например, величину печатаемого числа):

И ещё переменную i придётся инициализировать вне функции – например, так:

Вот теперь всё работает, как надо (мы уменьшили время задержки с 0.9 с до 0.4 с). Но для подобных задач логичнее всё-таки применять не setTimeout а setInterval (хотя при этом понадобится две функции):

Особенность метода Javascirpt setInterval в том, что он не проходит «сам собой», его надо останавливать специальным методом clearInterval. А чтобы было понятно, что именно останавливать, задаче по отложенному действию присваивается специальнй идентификатор – таймер: window.timer1 = window.setInterval(...).

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

Вот пример страницы с несколькими Javascript-таймерами, работающими одновременно: setinterval.htm (Javascript-функции в файле setinterval.js). Работу всех таймеров страницы (кроме меню) можно остановить клавишей Esc. Все таймеры примеров опираются на «естественный» (а не абстрактное i++) отсчёт – времени или расстояния. Все «часы» специально рассинхронизированы (для наглядности). Таймеры, зависящие от расстояния, используются в «индикаторе» и в выпадающем («выезжающем») меню.

Выпадающее меню

Наше выезжающее меню – реально выезжающее (из-под «шапки»): между элементами специально оставлены зазоры, чтобы видеть, как оно выезжает. Неожиданно оказалось, что мы не можем сделать одинаково плавный выезд для списков разной длины – вероятно, из-за низкой производительности компьютера (AMD Athlon 999 МГц).

Достаточно очевидно, что для красоты и гармонии нужно, чтобы списки разных пунктов меню выпадали за одно и то же время. То есть более длинные списки должны выпадать с более высокой скоростью, более короткие – с меньшей скоростью. Казалось бы, это можно реализовать так:

  1. Устанавливаем общее время «выезжания», например, в 200 мс.
  2. Если выпадающий список имеет высоту 20 px, очевидно, что мы можем двигать его вниз по одному пикселу за интервал 10 мс – и тогда за 200 мс список вылезет весь.
  3. Если выпадающий список имеет высоту 40 px, чтобы уложиться в то же время, мы должны двигать его вниз по одному пикселу за 5 мс.

По этой логике, если выпадающий список имеет высоту 200 px, мы должны двигать его вниз по одному пикселу за 1 мс. Но такая скорость на нашем компьютере не прокатывает – браузер просто не успевает отрисовывать новое положение списка за одну миллисекунду. Да. Javascript считать успевает (что там считать-то?), а браузер (Firefox) отображать не успевает. Типичная ситуация для веб.

Поэтому более-менее уровнять время выезжания меню можно только с помощью костылей, и ещё неясно, как это будет работать на более быстром компьютере. Но мы ведь должны рассчитывать на самый медленный? Алгоритм (без учёта быстродействия компьютера) получается примерно такой:

  1. Устанавливаем общее время выезжания списка: time = 224 (ms).
  2. Устанавливаем минимальное время для одного интервала в цикле: delay = 3 (ms).
  3. Устанавливаем минимальный шаг для движения списка: offset = 1 (px).
  4. Меняем всё это в зависимости от высоты списка: 1) увеличиваем время задержки (интервала) обратно пропорционально высоте и прямо пропорционально общему времени time (при высоте 224 коэффициент равен 1); 2) если высота больше 40 px, увеличиваем минимальный шаг пропорционально высоте. Константа "40" получена опытным путём для наиболее медленного компьютера. Тесты на компьютере Pentium 4 CPU 2.53GHz выявили точно такое же число – 40. Иначе таймеры идут вразнос, списки выезжают не в ногу.

Вот теперь списки более-менее выезжают. За более-менее похожее время. На странице setinterval.htm.

А вот и Брю-ус:

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

Ну, а перед запуском только вычислить все эти maxtop и offset, а также поместить список в положение mintop. Чем и занимается «предварительная» функция slide() размером в 40 строк. А всё вместе - в файле setinterval.js. Да, и эта хрень ни хрена не будет работать без подключенного файла стилей menuroll.css.

D.M., admin

сумки louis vuitton интернет магазин

194. Nataly

Долго перебирала примеры использования setInterval, setTimeout – все примеры очень примитивны и без толкового объяснения смысла. У меня на сайте setInterval, setTimeout никак работать не хотели. И только блоагодаря этой статье все стало наконец на свои места – автору большое спасибо.

28.01.2011 15:33:15

196. D.M., admin

Nataly, всегда пожалуйста. Обращайтесь с вопросами, если что!

31.01.2011 09:18:30

224. Михаил

Очень грамотное и полезное объяснение. Спасибо

22.04.2011 17:54:11

225. Вадим

спасибо большое)))

03.05.2011 00:16:15

275. Александр

Здраствуйте отличный материал очень полезный... А не подскажите как можно поставить setTimeout на паузу при неактивном окне браузера Или просто дабавить время

08.12.2011 10:32:03

276. D.M., admin

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

13.12.2011 08:27:03

277. JS-Ламер

function changeHeight(){ window.i=0; document.getElementById('Keys1').style.display="block"; window.ddd=window.setInterval(stopChange(), 400); }

function stopChange(){ document.getElementById('Keys1').style.height = i; i=i+1; if (i == 155) {clearInterval(window.ddd);}; }

почему выпадает только на 1 пиксель? я уж замучалсо

19.12.2011 02:18:36

279. JS-Ламер

всЕ, решил, спасибо большое за статью! :)

19.12.2011 19:53:31

282. дурачек

а если кнопку "приостановить" нажимать после достижения 100% то можно "продвигаться" дальше))))))

18.01.2012 23:15:21

284. soulman

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

27.01.2012 13:16:06

289. Donnie

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

09.02.2012 21:32:12

290. D.M., admin

Donnie, в функции showrever есть строка: d=d.getTime() + 24*60*60*1000

Именно эта строка определяет точку, до которой идёт отсчёт (в данном случае +24 часа). Если известна абсолютная точка, из неё можно создать Дату или Время с помощью объекта Date:

var d = new Date(); d.setTime(Date.parse("2012-03-04 12:22:23"));

(см. http://javascript.ru/Date.parse)

13.02.2012 09:32:00

310. ankhzet

"мы не можем сделать одинаково плавный выезд для списков разной длины" *facepalm

function roll_Init(rollElement, rollHeight, rollTime) { var now = (new Date()).getTime(); var roll = { element: rollElement, height : rollHeight, time : rollTime, start : now, end : now + rollTime, timer : null }; roll.timer = setInterval(function () {roll_Process(roll);}, 10); }

function roll_Process(roll) { var now = (new Date()).getTime(); if (now >= roll.end) roll_Stop(roll); else { var interpolation = roll.height * ((now – roll.start) / roll.time); roll.element.style.height = interpolation + 'px'; } }

function roll_Stop(roll) { clearInterval(roll.timer); roll.element.style.height = roll.height + 'px'; }

возможны синтаксические ошибки, написано на коленке. вне зависимости от производительности, в начале запуска эффекта – высота 0, через rollTime мсек – rollHeight. С каким интервалом не вызвался бы таймер, скорость увеличения высоты пропорциональна итоговой высоте. Промежуточная высота равна такой, какая была бы, если б таймер вызывался каждые rollTime / rollHeight, и высота при этом увеличивалась на 1. Для елементов любой высоты. И никаких танцев с бубном длиной в 40 строк -_-

05.04.2012 00:20:51

312. D.M., admin

ankhzet, а пример работающий посмотреть можно?

17.04.2012 17:51:22

Добавить комментарий:

*Автор:
E-Mail:
*Текст: