Лекция
Привет, сегодня поговорим про settimeout, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое settimeout, setinterval, декоратор debounce , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend).
setTimeout
setInterval
setInterval
setTimeout
setTimeout(func, 0)
Почти все реализации JavaScript имеют внутренний таймер-планировщик, который позволяет задавать вызов функции через заданный период времени.
В частности, эта возможность поддерживается в браузерах и в сервере Node.JS.
setTimeout
Синтаксис:
var timerId = setTimeout(func/code, delay[, arg1, arg2...]) |
Параметры:
func/code
Функция или строка кода для исполнения.
Строка поддерживается для совместимости, использовать ее не рекомендуется.
delay
Задержка в милисекундах, 1000 милисекунд равны 1 секунде.
arg1
, arg2
…
Аргументы, которые нужно передать функции. Не поддерживаются в IE9-.
Исполнение функции произойдет спустя время, указанное в параметре delay
.
Например, следующий код вызовет alert('Привет')
через одну секунду:
1 |
function func() { |
2 |
alert( 'Привет' ); |
3 |
} |
4 |
setTimeout(func, 1000); |
Если первый аргумент является строкой, то интерпретатор создает анонимную функцию из этой строки.
То есть такая запись работает точно так же:
1 |
setTimeout( "alert('Привет')" , 1000); |
Использование строк не рекомендуется, так как они могут вызвать проблемы при минимизации кода, и, вообще, сама возможность использовать строку сохраняется лишь для совместимости.
Вместо них используйте анонимные функции:
1 |
setTimeout( function () { alert( 'Привет' ) }, 1000); |
Во всех современных браузерах, с учетом IE10, setTimeout
позволяет указать параметры функции.
Пример ниже выведет "Привет, я Вася"
везде, кроме IE9-:
1 |
function sayHi(who) { |
2 |
alert( "Привет, я " + who); |
3 |
} |
4 |
5 |
setTimeout(sayHi, 1000, "Вася" ); |
…Однако, в большинстве случаев нам нужна поддержка старого IE, а он не позволяет указывать аргументы. Поэтому, для того, чтобы их передать, оборачивают вызов в анонимную функцию:
1 |
function sayHi(who) { |
2 |
alert( "Привет, я " + who); |
3 |
} |
4 |
5 |
setTimeout( function () { sayHi( 'Вася' ) } , 1000); |
Вызов через setTimeout
не передает контекст this
.
В частности, вызов метода объекта через setTimeout
сработает в глобальном контексте. Это может привести к некорректным результатам.
Например, вызовем user.sayHi()
через одну секунду:
01 |
function User(id) { |
02 |
this .id = id; |
03 |
04 |
this .sayHi = function () { |
05 |
alert( this .id); |
06 |
}; |
07 |
} |
08 |
09 |
var user = new User(12345); |
10 |
11 |
setTimeout(user.sayHi, 1000); // ожидается 12345, но выведет "undefined" |
Так как setTimeout
запустит функцию user.sayHi
в глобальном контексте, она не будет иметь доступ к объекту через this
.
Иначе говоря, эти два вызова setTimeout
делают одно и то же:
1 |
// (1) одна строка |
2 |
setTimeout(user.sayHi, 1000); |
3 |
4 |
// (2) то же самое в две строки |
5 |
var func = user.sayHi; |
6 |
setTimeout(func, 1000); |
К счастью, эта проблема также легко решается созданием промежуточной функции:
01 |
function User(id) { |
02 |
this .id = id; |
03 |
04 |
this .sayHi = function () { |
05 |
alert( this .id); |
06 |
}; |
07 |
} |
08 |
09 |
var user = new User(12345); |
10 |
11 |
setTimeout( function () { |
12 |
user.sayHi(); |
13 |
}, 1000); |
Функция-обертка используется, чтобы кросс-браузерно передать аргументы и сохранить контекст выполнения.
В следующих главах мы разберем дополнительные способы привязки функции к объекту.
Функция setTimeout
возвращает идентификатор timerId
, который можно использовать для отмены действия.
Синтаксис: clearTimeout(timerId)
.
В следующем примере мы ставим таймаут, а затем удаляем (передумали). В результате ничего не происходит.
1 |
var timerId = setTimeout( function () { alert(1) }, 1000); |
2 |
3 |
clearTimeout(timerId); |
setInterval
Метод setInterval
имеет синтаксис, аналогичный setTimeout
.
var timerId = setInterval(func/code, delay[, arg1, arg2...]) |
Смысл аргументов — тот же самый. Но, в отличие от setTimeout
, он запускает выполнение функции не один раз, а регулярно повторяет ее через указанный интервал времени. Остановить исполнение можно вызовом clearInterval(timerId)
.
Следующий пример при запуске станет выводить сообщение каждые две секунды, пока вы не нажмете на кнопку «Стоп»:
1 |
< input type = "button" onclick = "clearInterval(timer)" value = "Стоп" > |
2 |
3 |
setInterval
Вызов setInterval(функция, задержка)
ставит функцию
на исполнение через указанный интервал времени. Но здесь есть тонкость.
На самом деле пауза между вызовами меньше, чем указанный интервал.
Для примера, возьмем setInterval(function() { func(i++) }, 100)
. Она выполняет func
каждые 100 мс, каждый раз увеличивая значение счетчика.
На картинке ниже, красный блок - это время исполнения func
. Время между блоком — это время между запусками функции, и оно меньше, чем установленная задержка!
То есть, браузер инициирует запуск функции аккуратно каждые 100мс
, без учета времени выполнения самой функции.
Бывает, что исполнение функции занимает больше времени, чем задержка. Например, функция сложная, а задержка маленькая. Или функция содержит операторы alert/confirm/prompt
, которые блокируют поток выполнения. В этом случае начинаются интересные вещи
Если запуск функции невозможен, потому что браузер занят — она становится в очередь и выполнится, как только браузер освободится.
Изображение ниже иллюстрирует происходящее для функции, которая долго исполняется.
Вызов функции, инициированный setInterval
, добавляется в очередь и незамедлительно происходит, когда это становится возможным:
Второй запуск функции происходит сразу же после окончания первого:
Больше одного раза в очередь выполнение не ставится.
Если выполнение функции занимает больше времени, чем несколько запланированных исполнений, то в очереди она все равно будет стоять один раз. Так что «накопления» запусков не происходит.
На изображении ниже setInterval
пытается выполнить функцию в 200 мс и ставит вызов в очередь. В 300 мс и 400 мс таймер пробуждается снова, но ничего не просходит.
Давайте посмотрим на примере, как это работает.
Внутренний таймер в браузерах Safari/Chrome во время показаalert/confirm/prompt
не «тикает». Если до исполнения оставалось 3 секунды, то даже при показе alert
на протяжении минуты — задержка остается 3 секунды.
Поэтому пример ниже не воспроизводится в этих браузерах. В других браузерах все в порядке.
alert
. Пока модальное окошко отображается, исполнение JavaScript блокируется. Подождите немного и нажмите OK.Стоп
.
1 |
< input type = "button" onclick = "clearInterval(timer)" value = "Стоп" > |
2 |
3 |
Происходит следующее.
alert
— исполнение блокируется и остается заблокированным все время, пока alert
отображается.
Вызов setInterval(функция, задержка)
не гарантирует реальной задержки междуисполнениями.
Бывают случаи, когда реальная задержка больше или меньше заданной. Вообще, не факт, что будет хоть какая-то задержка.
setTimeout
В случаях, когда нужно не просто регулярное повторение, а обязательна задержка между запусками, используется повторная установка setTimeout
при каждом выполнении функции.
Ниже — пример, который выдает alert
с интервалами 2 секунды между ними.
01 |
< input type = "button" onclick = "clearTimeout(timer)" value = "Стоп" > |
02 |
03 |
На временной линии выполнения будут фиксированные задержки между запусками. Об этом говорит сайт https://intellect.icu . Иллюстрация для задержки 100мс:
У браузерного таймера есть минимальная возможная задержка. Она меняется от примерно нуля до 4мс в современных браузерах. В более старых она может быть больше и достигать 15мс.
По стандарту, минимальная задержка составляет 4мс. Так что нет разницы междуsetTimeout(..,1)
и setTimeout(..,4)
.
В поведении setTimeout
и setInterval
с нулевой задержкой есть браузерные особенности.
setTimeout(.., 0)
— то же самое, что setTimeout(.., 4)
. Оно выполняется реже, чем setTimeout(.. ,2)
. Это особенность данного браузера.setInterval(.., 0)
не сработает. Это касается именноsetInterval
, т.е. setTimeout(.., 0)
работает нормально.
В ряде случаев задержка может быть не 4мс, а 30мс или даже 1000мс.
setTimeout/setInterval
, даже если вкладка неактивна.
При этом ряд из них (Chrome, FF, IE10) снижают минимальную частоту таймера, до 1 раза в секунду. Получается, что в «фоновой» вкладке будет срабатывать таймер, но редко.
setInterval
будут пропущены.Вывод: на частоту 4мс стоит ориентироваться, но не стоит рассчитывать.
Посмотрим снижении частоты в действии на небольшом примере.
При клике на кнопку ниже запускается setInterval(..., 90)
, который выводит список интервалов времени между 25 последними срабатываниями таймера. Запустите его. Перейдите на другую вкладку и вернитесь.
Запустить повтор с интервалом в 90 мс
Остановить повтор
Если ваш браузер увеличивает таймаут при фоновом выполнении вкладки, то вы увидите увеличенные интервалы, помеченные красным.
Кроме того, вы точно увидите, что таймер не является идеально точным
Код, который используется в примере выше и считает интервалы времени между вызовами, выглядит примерно так:
01 |
var timeMark = new Date; |
02 |
setTimeout( function go() { |
03 |
var diff = new Date - timeMark; |
04 |
05 |
// вывести очередную задержку в консоль вместо страницы |
06 |
console.log(diff); |
07 |
08 |
// запомним время в самом конце, |
09 |
// чтобы измерить задержку именно между вызовами |
10 |
timeMark = new Date; |
11 |
12 |
setTimeout(go, 100); |
13 |
}, 100); |
Нулевой или небольшой таймаут также используют, чтобы разорвать поток выполнения «тяжелых» скриптов.
Например, скрипт для подсветки синтаксиса должен проанализировать код, создать много цветных элементов для подсветки и добавить их в документ — на большом файле это займет много времени.
Браузер сначала будет есть 100% процессора, а затем может выдать сообщение о том, что скрипт выполняется слишком долго.
Для того, чтобы этого избежать, сложная задача разбивается на части, выполнение каждой части запускается через мини-интервал после предыдущей, чтобы дать браузеру время. Например, планируется подсветка 20 строк каждые 10мс.
setTimeout(func, 0)
Этот трюк достоин войти в анналы JavaScript-хаков.
Функцию оборачивают в setTimeout(func, 0)
, если хотят запустить ее после окончания текущего скрипта.
Дело в том, что setTimeout
никогда не выполняет функцию сразу. Он лишь планирует ее выполнение. Но интерпретатор JavaScript начнет выполнять запланированные функции лишь после выполнения текущего скрипта.
По стандарту, setTimeout
в любом случае не может выполнить функцию с задержкой 0
. Как мы говорили раньше, обычно задержка составит 4мс. Но главное здесь именно то, что выполнение в любом случае будет после выполнения текущего кода.
Например:
01 |
var result; |
02 |
03 |
function showResult() { |
04 |
alert(result); |
05 |
} |
06 |
07 |
setTimeout(showResult, 0); |
08 |
09 |
result = 2*2; |
10 |
11 |
// выведет 4 |
декоратор debounce - это популярный паттерн проектирования в программировании, который используется для управления частотой выполнения функции. Он задерживает выполнение функции до тех пор, пока не пройдет определенное количество времени между последним вызовом функции и вызовом, который будет реально выполнен. Этот паттерн полезен, например, при обработке событий, которые могут происходить с высокой частотой, и вы хотите оптимизировать их обработку.
Вот пример реализации декоратора debounce на языке JavaScript:
function debounce(func, delay) { let timeoutId; return function() { const context = this; const args = arguments; clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(context, args); }, delay); }; } // Пример использования: function doSomething() { console.log("Выполнилось!"); } const debouncedFunction = debounce(doSomething, 1000); // Вызов debounce с функцией и временем задержки // Теперь debouncedFunction можно вызывать, и она будет выполняться только один раз, если не вызывать ее вновь в течение 1 секунды. debouncedFunction(); // Не выполнится немедленно debouncedFunction(); // Не выполнится немедленно // Подождите 1 секунду // "Выполнилось!" будет выведено в консоль только один раз после задержки
Позже, в главе Управление порядком обработки, setTimeout(…0), мы рассмотрим различные применения этого трюка при работе с событиями.
Методы setInterval(func, delay)
и setTimeout(func, delay)
позволяют запускать func
регулярно/один раз через delay
миллисекунд.
Оба метода возвращают идентификатор таймера. Его используют для остановки выполнения вызовомclearInterval/clearTimeout
.
setInterval |
setTimeout |
|
Тайминг | Идет вызов строго по таймеру. Если интерпретатор занят — один вызов становится в очередь.
Время выполнения функции не учитывается, поэтому промежуток времени от окончания одного запуска до начала другого может быть различным. |
Рекурсивный вызов setTimeout используется вместо setInterval там, где нужна фиксированная пауза между выполнениями. |
Задержка | Минимальная задержка: 4мс. | Минимальная задержка: 4мс. |
Минимальная задержка для этих методов в современных браузерах различна и колеблется от примерно нуля до 4мс. В старых браузерах она может доходить до 15мс. | ||
Браузерные особенности | В IE не работает задержка 0 . |
В Opera нулевая задержка эквивалентна 4мс, остальные задержки обрабатываются точно, в том числе нестандартные 1мс, 2мс и 3мс. |
К сожалению, в одной статье не просто дать все знания про settimeout. Но я - старался. Если ты проявишь интерес к раскрытию подробностей,я обязательно напишу продолжение! Надеюсь, что теперь ты понял что такое settimeout, setinterval, декоратор debounce и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)
Комментарии
Оставить комментарий
Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)
Термины: Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)