Практика JavaScript и PHP | Вы думаете, setTimeout останавливает работу Javascript? Нет, таймеры в Javascript работают по-другому: они откладывают на определённое время только одну задачу. |
учебник HTML скачать
| Таймеры в 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 мс (то есть несоизмеримо мало, по сравнению с 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 МГц). Достаточно очевидно, что для красоты и гармонии нужно, чтобы списки разных пунктов меню выпадали за одно и то же время. То есть более длинные списки должны выпадать с более высокой скоростью, более короткие – с меньшей скоростью. Казалось бы, это можно реализовать так:
По этой логике, если выпадающий список имеет высоту 200 px, мы должны двигать его вниз по одному пикселу за 1 мс. Но такая скорость на нашем компьютере не прокатывает – браузер просто не успевает отрисовывать новое положение списка за одну миллисекунду. Да. Javascript считать успевает (что там считать-то?), а браузер (Firefox) отображать не успевает. Типичная ситуация для веб. Поэтому более-менее уровнять время выезжания меню можно только с помощью костылей, и ещё неясно, как это будет работать на более быстром компьютере. Но мы ведь должны рассчитывать на самый медленный? Алгоритм (без учёта быстродействия компьютера) получается примерно такой:
Вот теперь списки более-менее выезжают. За более-менее похожее время. На странице 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 Добавить комментарий: |