Таймеры в JavaScript

В JavaScript есть внутренний таймер, который предоставляет возможность запускать функции через определённое время. Для этого используются функции setTimeout() или setInterval(). Они не имеют собственного объекта, а являются методами объекта window.

setTimeout()

Метод setTimeout() применяется, когда необходимо однократно запустить какую-либо функцию через заданный промежуток времени. Синтаксис setTimeout() представлен двумя формами записи на выбор.

Основная форма:

setTimeout(функция[, задержка, аргумент1, аргумент2, ...])

Альтернативная форма:

setTimeout(код[, задержка])
функция
Имя функции, которая должна быть запущена. Функцию можно задать непосредственно при вызове setTimeout().
код
Строка кода, которая будет преобразована в анонимную функцию.
задержка
Необязательный параметр. Время в миллисекундах, через которое должна выполниться функция. По умолчанию устанавливается 0.
аргумент1, аргумент2, ...
Аргументы, которые будут переданы в функцию.

В примере, приведённом ниже, представлены два вызова метода setTimeout(), которые приведут к одному результату.

setTimeout(function() {
  alert('Прошла 1 секунда');
}, 1000);
// или
setTimeout("alert('Прошла 1 секунда')", 1000);

Альтернативная форма не рекомендуется для использования. Для этого есть много причин, но основные - это потеря производительности и возможные проблемы при минимизации кода. Эта форма существует только для совместимости со старыми скриптами.

Метод setTimeout() возвращает идентификатор таймера. Он используется для отмены таймера.

Вызов setTimeout() устанавливает задержку только для переданной функции. Всё, что следует за этим методом, продолжает выполняться без паузы во времени.

Отмена setTimeout

Для отмены действия setTimeout() используется метод clearTimeout().

clearTimeout(идентификатор)
идентификатор
Идентификатор таймера, полученный при вызове setTimeout().

Пример использования setTimeout

<html>
<head>
  <title>BOM-интерфейс</title>
</head>
<body>
  <button>Заморозить на 3 секунды</button>
</body>
</html>
 
<script>
var button = document.body.firstElementChild;
button.onclick = function() {
  button.disabled = true;
  setTimeout(function() {  /* запуск таймера */
    button.disabled = false;
  }, 3000);
};
</script>

setInterval()

Метод setInterval() используется для многократного запуска определённой функции через заданный промежуток времени. Синтаксис этого метода полностью аналогичен setTimeout().

Основная форма:

setInterval(функция[, задержка, аргумент1, аргумент2, ...])

Альтернативная форма:

setInterval(код[, задержка])
функция
Имя функции, которая будет повторяться. Функцию можно задать непосредственно при вызове setInterval().
код
Строка кода, которая будет преобразована в анонимную функцию.
задержка
Необязательный параметр. Время в миллисекундах, задающее интервал между запусками функции. По умолчанию устанавливается 0.
аргумент1, аргумент2, ...
Аргументы, которые будут переданы в функцию при каждом запуске.

Ниже приведён пример, в котором оба вызова метода setInterval() приведёт к одному результату.

setInterval(function() {
  alert('Прошла 1 секунда');
}, 1000);
// или
setInterval("alert('Прошла 1 секунда')", 1000);

Альтернативная форма не рекомендуется для использования (причины указаны в описании setTimeout()).

Метод setInterval() возвращает идентификатор таймера. Он используется для отмены таймера.

Вызов setInterval() устанавливает интервал только для переданной функции. Весь код, который следует за этим методом, продолжает выполняться без паузы во времени.

Отмена setInterval

Метод setInterval() будет запускать переданную функцию до тех пор, пока не будет принудительно остановлен. Для этого используется метод clearInterval().

clearInterval(идентификатор)
идентификатор
Идентификатор таймера, полученный при вызове setInterval().

Пример использования setInterval

<html>
<head>
  <title>BOM-интерфейс</title>
</head>
<body>
  <button>Заморозить на 5 секунд</button>
</body>
</html>
 
<script>
var button = document.body.firstElementChild;
button.onclick = function() {
  var time = 5;
  button.disabled = true;
  button.innerHTML = 'Разморозится через ' + time;
  var interval_id = setInterval(function() {  /* запуск таймера */
    time--;
    if (time != 0) {
      button.innerHTML = 'Разморозится через ' + time;
    } else {
      clearInterval(interval_id);  /* остановка таймера */
      button.disabled = false;
      button.innerHTML = 'Заморозить на 5 секунд';
    }
  }, 1000);
};
</script>

Особенности использования таймеров

Минимальная задержка ограничена

Теоретически в таймерах можно указывать нулевую задержку времени. Однако, функция всё равно будет запускаться с некоторой задержкой. В действующем стандарте минимальная задержка установлена в 4 мс. В современных браузерах она составляет от 4 до 10 мс. Это сделано для того, чтобы вызов setInterval() с нулевой задержкой не привёл к перегрузке процессора или сервера.

Максимальная задержка ограничена

Большинство браузеров хранят время задержки таймера в виде 32-битного целого числа. Это означает, что при использовании значения больше, чем 2147483647 (примерно 25 дней), произойдёт переполнение памяти и таймер запустится без задержки.

Задержка имеет погрешность

Задержка может отличаться от установленной из-за загрузки системы другими процессами. В этом можно убедиться на примере, представленном ниже. Задержка установлена 100 мс:

Задержка может ограничиваться браузером

В неактивных закладках задержка может сильно отличаться от заданной. Многие браузеры в целях экономии ресурсов для неактивных закладок устанавливают минимальную задержку в 1000 мс. Это будет видно, если при запущенном примере, приведённом выше, на несколько секунд перейти в другую вкладку.

Влияние функции на интервал

Задержка, установленная в таймере, задаёт интервал между запусками функции. Но заданная функция всегда выполняется в течение некоторого времени. Это время никак не влияет на заданный интервал, если оно меньше интервала. Выполнение функции заканчивается раньше, чем требуется её повторный запуск.

Влияние функции на интервал

Если время выполнения функции окажется больше, чем заданный интервал, тогда повторный запуск не будет осуществлён вовремя. Таймер будет ждать, пока функция закончит выполнение, а после этого сразу же запустит её снова (без задержки). При таком варианте фактический интервал может сильно отличаться от заданного.

Влияние функции на интервал

Всё вышесказанное подтверждается следующим примером. Интервал таймера задан 50 мс. Время выполнения функции можно варьировать от 0 до 200 мс. Если введённое значение меньше 50, тогда интервал таймера будет соответствовать заданному (с некоторой погрешностью, про которую писалось выше). А если больше 50, тогда интервал между запусками будет равен времени выполнения функции.



Бывают случаи, когда необходимо установить интервал не между запусками функции, а от окончания выполнения функции до повторного запуска, то есть обязательно обеспечить паузу между выполнениями функции. Должна получиться такая картина:

Влияние функции на интервал

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

var interval_id = setInterval(action, 100);

function action() {
  // действие функции
}

Тогда его можно переписать так:

var timeout_id = setTimeout(action, 100);

function action() {
  // действие функции
  timeout_id = setTimeout(action, 100);
}

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

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



Функция:

Задержка: