JavaScript метод WindowOrWorkerGlobalScope.setInterval()
Определение и применение
JavaScript метод setInterval() объекта
WindowOrWorkerGlobalScope
многократно вызывает функцию или выполняет фрагмент кода с фиксированной задержкой времени между каждым вызовом.
Метод setInterval() возвращает положительное числовое целое ненулевое значение, которое определяет
таймер, это значение может быть передано методу
clearInterval(), чтобы отменить выполнение программного кода, ранее отложенного вызовом метода setInterval().
Идентификатор уникален среди активных таймеров одного глобального контекста. После отмены или завершения таймера его номер может использоваться снова; разные окна и воркеры имеют независимые наборы идентификаторов.
Пул идентификаторов, используемых методами
setTimeout()
и setInterval() являются общими, что означает, что вы можете технически использовать методы
clearTimeout()
и
clearInterval()
взаимозаменяемо. Однако для ясности вам следует избегать этого.
Задержка задаёт минимальное ожидание, а не точное время выполнения. Занятый основной поток, вложенность таймеров и
фоновые ограничения могут отложить вызов функции. При delay = 0 текущий синхронный код всё равно
завершится раньше.
Поддержка браузерами
Спецификация: multipage
Поддерживается во всех основных браузерах с 2015 года.
| Возможность | Компьютеры | Мобильные | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
setInterval | 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 intervalID = globalThis.setInterval(callback, delay);
const timerWithArguments = globalThis.setInterval(callback, delay, param1, param2);
const timerWithCode = globalThis.setInterval(code, delay); // не рекомендуемый синтаксис
Спецификация
Значения параметров
| Параметр | Описание |
|---|---|
callback |
Функция, которая будет многократно выполняться через заданный интервал. Обязательный параметр. |
delay |
Интервал в миллисекундах между вызовами указанной функции или кода. Если этот параметр опущен, то используется значение 0, оно означает, что выполнение должно произойти немедленно, или, точнее, как можно скорее (как только завершат работу все обработчики событий). Минимум 4 мс применяется после более чем пяти уровней вложенности таймеров. Обратите внимание, что в любом случае фактическая задержка может быть больше, чем предполагалось, причины задержек перечислены ниже в примерах. Необязательный параметр. |
param1, ..., paramN |
Дополнительные аргументы, которые передаются функции callback при каждом вызове. В строковом
синтаксисе code они игнорируются. Необязательные параметры.
|
code |
Альтернативный синтаксис, который позволяет включать строку вместо функции, которая компилируется и
выполняется по истечении таймера. Этот синтаксис не рекомендован к использованию в связи с угрозой
безопасности; при политике безопасности контента (CSP) без 'unsafe-eval' строка не выполняется.
|
Для опроса сервера планируйте следующий setTimeout() после завершения запроса, например после
await fetch(...). Один лишь перенос запуска запроса в рекурсивный таймер не исключает перекрытие
запросов.
Примеры использования
Базовое использование
Счётчик на setInterval() и его остановка
Нажмите , затем первую кнопку: счётчик будет увеличиваться каждую секунду. Вторая кнопка остановит интервал через clearInterval() и сбросит счётчик. Измените интервал 1000 в main.js и сравните скорость.
index.html
<button data-migration-click-0="">Run counter</button>
<button data-migration-click-2="">Clear timeout and reset counter</button>
<p id="counter">0</p>
main.js
let timeoutID
let counter = 0
function showCounter() {
counter++
document.getElementById('counter').innerHTML = counter
}
function clearMyTimeOut() {
clearInterval(timeoutID)
counter = 0
document.getElementById('counter').innerHTML = counter
}
function runCounter() {
clearInterval(timeoutID) // Повторное нажатие не создаёт второй таймер.
timeoutID = setInterval(showCounter, 1000)
}
// Обработчики кнопок перенесены из HTML для запуска с CSP без inline-скриптов.
document.querySelector('[data-migration-click-0]').addEventListener('click', function () {
runCounter()
})
document.querySelector('[data-migration-click-0]').removeAttribute('data-migration-click-0')
document.querySelector('[data-migration-click-2]').addEventListener('click', function () {
clearMyTimeOut()
})
document.querySelector('[data-migration-click-2]').removeAttribute('data-migration-click-2')
Нажмите , чтобы запустить пример.
В этом примере с использованием метода
addEventListener()
при нажатии на первую кнопку (HTML элемент
<button>) вызываем функцию runCounter(), которая с использованием JavaScript метода
setInterval() задает таймер, который многократно выполняет функцию showCounter() каждые 1000
миллисекунд (1 секунда).
Функция showCounter() в свою очередь инкрементирует (увеличивает на 1) значение переменной
counter, с помощью метода
getElementById()
находит элемент с определенным глобальным атрибутом
id
и изменяет его содержимое значением переменной counter.
С использованием метода
addEventListener()
при нажатии на вторую кнопку (HTML элемент
<button>) вызываем функцию clearMyTimeOut(), которая с использованием JavaScript метода
clearInterval()
отменяет повторяющееся выполнение программного кода, которое было ранее установлено вызовом метода
setInterval(), устанавливает значение переменной counter равное нулю и добавляет
элементу
<span>
это содержимое.
Проблема с this
Когда вы передаете методу setInterval() (или любой другой функции) метод, то он будет вызываться со
значением this, которое может отличаться от вашего ожидания.
Браузер вызывает обычную функцию таймера с this, равным window, даже в строгом режиме; в
worker это глобальный объект worker. Передача метода объекта отдельно не сохраняет его владельца. Стрелка сохраняет
внешний this, а bind() позволяет явно связать обычную функцию с объектом. Рассмотрим пример:
Контекст объекта в повторяющемся вызове
Нажмите : прямой вызов object.print() выведет «Привет», а повторяющиеся вызовы setInterval(object.print, 500) — undefined, потому что функция вызывается без объекта.
index.html
<p>Результат отложенного вызова появится в консоли.</p>
main.js
const object = {
value: 'Привет',
print() {
console.log(this.value)
},
}
object.print() // Вызов через объект: this — object, выводится «Привет».
const timer = setInterval(object.print, 500) // Передана только функция: this — window, выводится undefined.
setTimeout(() => clearInterval(timer), 1600) // Завершаем повторение.
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
При передаче метода в setInterval() передаётся только функция, поэтому this внутри неё
указывает на window, и свойство value не найдено. Оптимальным и самым простым решением этой
проблемы в данном случае будет использование анонимной функции обертки:
Сохранение контекста в повторяющемся вызове
Нажмите : стрелочная функция вызывает object.print() через объект, поэтому каждые полсекунды в консоли появляется «Привет»; через 1,6 секунды интервал останавливается.
index.html
<p>Результат отложенного вызова появится в консоли.</p>
main.js
const object = {
value: 'Привет',
print() {
console.log(this.value)
},
}
const timer = setInterval(() => object.print(), 500) // Сохраняем обращение к объекту.
setTimeout(() => clearInterval(timer), 1600) // Завершаем повторение.
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
Передача строковых литералов
Метод setInterval() имеет альтернативный синтаксис, который позволяет включать строку вместо функции,
которая компилируется и выполняется по истечении таймера:
// не рекомендованный синтаксис
setInterval("console.log('hello');", 1000);
// рекомендованный синтаксис
setInterval(myFunc, 1000);
setInterval(function() { console.log("hello"); }, 1000);
setInterval(() => { console.log("hello"); }, 1000); // допускается использование стрелочных функций
Максимальная задержка выполнения
Задержка преобразуется в 32-битное целое со знаком. Значения больше 2147483647 мс (около 24,8 дня) могут
переполниться и превратиться в неожиданную, в том числе нулевую, задержку. Для дальнего срока храните время выполнения
отдельно и проверяйте его короткими таймерами.
Причины увеличения задержек
Давайте рассмотрим распространенные причины, по которым тайм-аут может занять больше времени, чем ожидалось.
После пяти уровней вложенности таймеров задержка меньше 4 мс повышается до 4 мс. Это относится и к последовательным
срабатываниям setInterval(). У первого таймера нет универсальной минимальной задержки 4 мс; точное время
запуска не гарантируется.
В фоновых вкладках браузер может существенно увеличивать задержки, группировать таймеры или приостанавливать их. Единого гарантированного интервала в 1000 мс нет: политика зависит от браузера и состояния страницы.
На увеличение задержки также может повлиять, когда страница (или сама ОС/браузер) занята другими задачами. Важно
отметить, что функция или фрагмент кода не могут быть выполнены до тех пор, пока поток, вызвавший
setInterval() не завершится, например:
Интервал с нулевой задержкой
Нажмите и сравните порядок сообщений: «Отложенный вызов» выводится после «Конец синхронного кода», хотя интервал равен 0. Функция останавливает интервал после первого вызова.
index.html
<p>Результат отложенного вызова появится в консоли.</p>
main.js
console.log('Начало')
const timer = setInterval(() => {
console.log('Отложенный вызов')
clearInterval(timer) // Останавливаем интервал после первого вызова.
}, 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%;
}
Нажмите , чтобы запустить пример.
В этом примере не смотря на то, что setInterval() был вызван с нулевой задержкой, он был помещен в
очередь и запланирован для запуска при следующей возможности, а не сразу. Текущий код должен быть завершен до
выполнения функций в очереди, по этой причине порядок выполнения может быть не таким, как ожидалось.
Выполнение дольше интервала
Если существует вероятность того, что выполнение логики может занять больше времени, чем интервал времени, то
рекомендуется вместо использования метода setInterval() рекурсивно вызвать именованную функцию с помощью
метода
setTimeout(). Например, при использовании setInterval() для опроса удаленного сервера каждые 5 секунд задержка в
сети, не отвечающий сервер и множество других проблем могут помешать выполнению запроса в отведенное время. Таким
образом, вы можете оказаться в очереди с запросами XHR, которые не обязательно вернутся в необходимом
порядке. В этих случаях предпочтителен рекурсивный вызов метода
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()
после завершения выполнения логики. Хотя этот шаблон и не гарантирует выполнение на фиксированном интервале, но он
гарантирует, что предыдущий интервал был завершен до рекурсии.