JavaScript метод WindowOrWorkerGlobalScope.setTimeout()
Определение и применение
JavaScript метод setTimeout() объекта
WindowOrWorkerGlobalScope
задает таймер, который выполняет функцию или указанный фрагмент кода по истечению срока заданного тайм-аута.
Метод setTimeout() возвращает положительное целое числовое значение, которое определяет таймер, это
значение может быть передано методу
clearTimeout(), чтобы отменить выполнение программного кода, ранее отложенного вызовом метода setTimeout().
Идентификатор уникален среди активных таймеров одного глобального контекста. После отмены или завершения таймера его номер может использоваться снова; разные окна и воркеры имеют независимые наборы идентификаторов.
Пул идентификаторов, используемых методами setTimeout() и
setInterval()
являются общими, что означает, что вы можете технически использовать методы
clearTimeout()
и
clearInterval()
взаимозаменяемо. Однако для ясности вам следует избегать этого.
Задержка задаёт минимальное ожидание, а не точное время выполнения. Занятый основной поток, вложенность таймеров и
фоновые ограничения могут отложить вызов функции. При delay = 0 текущий синхронный код всё равно
завершится раньше.
Поддержка браузерами
Спецификация: multipage
Поддерживается во всех основных браузерах с 2015 года.
| Возможность | Компьютеры | Мобильные | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
setTimeout | 1 | 12 | 1 | 4 | 1 | 18 | 4 | 10.1 | 1 | 1.0 | 4.4 | 1 |
| code parameter requires TrustedScript instance when trusted types are enforced. | 83 | 83 | 148 | 69 | 26 | 83 | 148 | 59 | 26 | 13.0 | 83 | 26 |
| Supports parameters for callback | 1 | 12 | 1 | ≤15 | 1.2 | 18 | 4 | ≤14 | 1 | 1.0 | 4.4 | 1 |
| Available in workers | 3 | 12 | 3.5 | ≤12.1 | 4 | 18 | 4 | ≤12.1 | 5 | 1.0 | 4.4 | 5 |
поддерживаетсяс ограниченияминет поддержкинет данных≤31 — не позднее версии 31
JavaScript синтаксис
const timeoutID = globalThis.setTimeout(callback, delay);
const timerWithArguments = globalThis.setTimeout(callback, delay, param1, param2);
const timerWithCode = globalThis.setTimeout(code, delay); // не рекомендуемый синтаксис
Спецификация
Значения параметров
| Параметр | Описание |
|---|---|
callback |
Функция, которая будет выполняться после истечения таймера. Обязательный параметр. |
delay |
Время в миллисекундах, которое таймер должен ждать перед выполнением указанной функции или кода. Если этот параметр опущен, то используется значение 0, оно означает, что выполнение должно произойти немедленно, или, точнее, как можно скорее (как только завершат работу все обработчики событий). Минимум 4 мс применяется после более чем пяти уровней вложенности таймеров. Обратите внимание, что в любом случае фактическая задержка может быть больше, чем предполагалось, причины задержек перечислены ниже в примерах. Необязательный параметр. |
param1, ..., paramN |
Дополнительные аргументы, которые передаются функции callback при её вызове по истечении таймера.
В строковом синтаксисе code они игнорируются. Необязательные параметры.
|
code |
Альтернативный синтаксис, который позволяет включать строку вместо функции, которая компилируется и
выполняется по истечении таймера. Этот синтаксис не рекомендован к использованию в связи с угрозой
безопасности; при политике безопасности контента (CSP) без 'unsafe-eval' строка не выполняется.
|
Примеры использования
Базовое использование
Отложенное сообщение и отмена таймера
Нажмите ▶, затем Show delayed info: через 2 секунды в консоли появится сообщение. Нажмите Clear timeout в течение 2 секунд после первой кнопки — сообщение не появится. Измените задержку 2000 в main.js и сравните.
index.html
<button data-example-event-0="">Show delayed info</button>
<!-- добавляем атрибут событий onclick -->
<button data-example-event-1="">Clear timeout</button>
<!-- добавляем атрибут событий onclick -->
main.js
let timeoutID // создаем пустую переменную
function delayedInfo() {
timeoutID = setTimeout(function () {
console.log('Текстовое сообщение через 2 секунды')
}, 2000) // задаем таймер, который выполняет анонимную функцию по истечению 2000 миллисекунд (2 секунды)
}
function clearMyTimeOut() {
console.log('Таймер с id ' + timeoutID + ' отменен') // выводим в консоль значение переменной и информацию об отмене таймера
clearTimeout(timeoutID) // отменяет выполнение программного кода, ранее отложенного вызовом метода setTimeout
}
document.querySelector('[data-example-event-0]').addEventListener('click', function (event) {
delayedInfo()
})
document.querySelector('[data-example-event-1]').addEventListener('click', function (event) {
clearMyTimeOut()
})
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
В этом примере с использованием метода
addEventListener()
при нажатии на первую кнопку (HTML элемент
<button>) вызываем функцию delayedInfo(), которая с использованием JavaScript метода
setTimeout() задает таймер, который выполняет анонимную функцию по истечению 2000 миллисекунд (2
секунды).
С использованием метода
addEventListener()
при нажатии на вторую кнопку (HTML элемент
<button>) вызываем функцию clearMyTimeOut(), которая с использованием JavaScript метода
clearTimeout()
отменяет выполнение программного кода, ранее отложенного вызовом метода setTimeout(). Попробуйте после
нажатия на первую кнопку сразу нажать на вторую (в течении 2 секунд), как вы можете убедиться в этом
случае вызов отложенной функции не произойдет.
Проблема с this
Когда вы передаете методу setTimeout() (или любой другой функции) метод, то он будет вызываться со
значением this, которое может отличаться от вашего ожидания.
Код, выполняемый setTimeout(), вызывается из контекста выполнения, отдельного от функции, из которой был
вызван setTimeout(). Обычные правила установки контекста для ключевого слова this для
вызываемой функции применяются, и если вы не установили его явно в вызове или с определенной привязкой, то он по
умолчанию будет глобальным (или оконным) объектом в нестрогом режиме или будет неопределенным в строгом режиме. Он не
будет таким же, как this для функции, которая вызвала метод setTimeout(). Значение
this по умолчанию метода setTimeout() по-прежнему будет объектом window, а не
неопределенным, даже в строгом режиме. Давайте рассмотрим следующий пример:
Потеря контекста объекта при вызове
Нажмите ▶: прямой вызов object.print() выведет «Привет», а отложенный setTimeout(object.print, 500) — undefined, потому что функция вызывается без объекта.
index.html
<p>Результат отложенного вызова появится в консоли.</p>
main.js
const object = {
value: 'Привет',
print() {
console.log(this.value)
},
}
object.print() // Вызов через объект: this — object, выводится «Привет».
setTimeout(object.print, 500) // Передана только функция: this — window, выводится undefined.
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
При передаче метода в setTimeout() передаётся только функция, поэтому this внутри неё
указывает на window, и свойство value не найдено. Оптимальным и самым простым решением этой
проблемы в данном случае будет использование анонимной функции обертки:
Сохранение контекста через стрелочную функцию
Нажмите ▶: стрелочная функция вызывает object.print() через объект, поэтому через полсекунды в консоли появится «Привет». Замените обёртку на object.print и сравните результат.
index.html
<p>Результат отложенного вызова появится в консоли.</p>
main.js
const object = {
value: 'Привет',
print() {
console.log(this.value)
},
}
setTimeout(() => object.print(), 500) // Сохраняем обращение к объекту.
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
Передача строковых литералов
Метод setTimeout() имеет альтернативный синтаксис, который позволяет включать строку вместо функции,
которая компилируется и выполняется по истечении таймера:
// не рекомендованный синтаксис
setTimeout("console.log('hello');", 1000);
// рекомендованный синтаксис
setTimeout(myFunc, 1000);
setTimeout(function() { console.log("hello"); }, 1000);
setTimeout(() => { console.log("hello"); }, 1000); // допускается использование стрелочных функций
Максимальная задержка выполнения
Браузеры хранят значение задержки как 32-разрядное целое число со знаком. Значения выше 2147483647 миллисекунд (около 24,8 дней) вызовут переполнение, в результате чего вызов переданной функции произойдет немедленно.
Причины увеличения задержек
Давайте рассмотрим распространенные причины, по которым тайм-аут может занять больше времени, чем ожидалось.
В современных браузерах вызовы методов setTimeout() или
setInterval()
могут быть произведены как минимум один раз в 4 миллисекунды, когда последовательные вызовы инициируются из-за
вложенности обратного вызова, или после определенного количества последовательных интервалов.
Чтобы уменьшить нагрузку (и связанное с ней использование батареи) с фоновых вкладок, тайм-ауты регулируются до срабатывания не чаще одного раза в секунду (1000 миллисекунд) в неактивных вкладках.
На увеличение задержки также может повлиять, когда страница (или сама ОС/браузер) занята другими задачами. Важно
отметить, что функция или фрагмент кода не могут быть выполнены до тех пор, пока поток, вызвавший
setTimeout() не завершится, например:
Нулевая задержка и синхронный код
Нажмите ▶ и сравните порядок сообщений в консоли: «Отложенный вызов» выводится после «Конец синхронного кода», хотя задержка равна 0.
index.html
<p>Результат отложенного вызова появится в консоли.</p>
main.js
console.log('Начало')
setTimeout(() => console.log('Отложенный вызов'), 0)
console.log('Конец синхронного кода')
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
В этом примере не смотря на то, что setTimeout() был вызван с нулевой задержкой, он был помещен в очередь
и запланирован для запуска при следующей возможности, а не сразу. Текущий код должен быть завершен до выполнения
функций в очереди, по этой причине порядок выполнения может быть не таким, как ожидалось.
Микрозадачи и таймер
Микрозадачи, например функции, переданные в queueMicrotask(), и обработчики Promise,
выполняются сразу после текущего синхронного кода, но раньше функции таймера, даже если задержка равна нулю:
Микрозадача выполняется перед таймером
Нажмите ▶ и сравните порядок: сначала синхронный код, затем «Микрозадача» и только потом «Таймер». Поменяйте местами вызовы setTimeout() и queueMicrotask() — порядок вывода не изменится.
index.html
<p>Результат отложенного вызова появится в консоли.</p>
main.js
console.log('Начало')
setTimeout(() => console.log('Таймер'), 0)
queueMicrotask(() => console.log('Микрозадача'))
console.log('Конец синхронного кода')
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
Функция, переданная в queueMicrotask(), выполняется сразу после завершения текущего синхронного кода, а
функция таймера — только в следующей задаче. Поэтому сообщение «Микрозадача» всегда выводится раньше сообщения
«Таймер».
Выполнение дольше интервала
Если существует вероятность того, что выполнение логики может занять больше времени, чем интервал времени, то
рекомендуется вместо использования метода
setInterval()
рекурсивно вызвать именованную функцию с помощью метода setTimeout(). Например, при использовании
setInterval()
для опроса удаленного сервера каждые 5 секунд задержка в сети, не отвечающий сервер и множество других проблем могут
помешать выполнению запроса в отведенное время. Таким образом, вы можете оказаться в очереди с запросами
XHR, которые не обязательно вернутся в необходимом порядке. В этих случаях предпочтителен рекурсивный
вызов метода setTimeout():
Последовательные вызовы через setTimeout
Нажмите ▶: три шага выводятся с паузой 500 мс, следующий запуск назначается только после завершения предыдущего. Измените условие count < 3, чтобы получить больше шагов.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
let count = 0
function loop() {
console.log('Шаг', ++count)
// Следующий запуск назначается после завершения текущей работы.
if (count < 3) setTimeout(loop, 500)
}
loop()
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
В приведенном выше фрагменте объявлена именованная функция loop(), которая немедленно выполняется, эта
функция рекурсивно вызывается внутри метода setTimeout() после завершения выполнения логики. Хотя этот
шаблон и не гарантирует выполнение на фиксированном интервале, но он гарантирует, что предыдущий интервал был завершен
до рекурсии.