На этом уроке мы познакомимся с таймерами, которые предназначены для вызова кода на языке JavaScript через определённые промежутки времени.

setTimeout и setInterval

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

Отличаются они друг от друга лишь тем, что setTimeout выполняет вызов функции всего один раз, а setInterval – постоянно через указанный интервал времени.

Синтаксис setTimeout и setInterval:

// setTimeout
window.setTimeout(func [, delay] [, arg1, arg2, ...]);
// setInterval
window.setTimeout(func [, delay] [, arg1, arg2, ...]);

Параметры:

  • func – функция, которую нужно вызвать спустя указанное в delay количество миллисекунд;
  • delay – количество миллисекунд по истечении которых нужно выполнить func;
  • arg1, arg2, ... – дополнительные аргументы, которые нужно передать в func.

Например, вызовем функцию myFunc один раз по прошествии 3 секунд:

// функция
function myFunc() {
  console.log('after 3 seconds');
}

// вызовем функцию myFunc после 3 секунд
window.setTimeout(myFunc, 3000);
// выведем сообщение в консоль
console.log('immediately');

При выполнение этого кода программа не остановится на месте, где мы зарегистрировали некоторую асинхронность посредством setTimeout и не будет блокировать её дальнейшее выполнение на 3 секунды, через которые нужно будет вызвать myFunc. Выполнение скрипта продолжится дальше и сначала браузер выведет в консоль сообщение «immediately», а затем через 3 секунды – «after 3 seconds».

Другими словами, когда браузер доходит до исполнения setTimeout, он как бы помещает функцию myFunc в некоторое место, а затем по прошествии определённого количества времени её вызывает. При этом блокировка основного потока выполнения программы не происходит.

Чтобы более подробно разобраться в этом необходимо рассмотреть, как движок JavaScript выполняет синхронный и асинхронный код, а также что такое event loop и как он работает.

Синхронный и асинхронный код

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

Выполнение такого кода движок JavaScript выполняет последовательно (т.е. строчку за строчкой). При этом перед тем, как выполнить какую-то строчку кода интерпретатор сначала помещает её в стек вызовов (call stack). Именно в нём происходит её разбор и исполнение. После этого происходит её извлечение из стека и переход к следующей строчке.

Но всё меняется, когда интерпретатор доходит до выполнения асинхронных операций, например setTimeout. Они также как и синхронные операции сначала попадают в стек вызовов, где происходит их разбор. Но, при разборе интерпретатор понимает, что это некоторый вызов Web API и помещает эту операцию в него. После этого он удаляет эту строчку из call stack и переходит к выполнению следующей строчки кода.

В это же время Web API регистрирует эту функцию и запускает таймер. Как только он завершается, он помещает эту функцию в очередь (callback queue). Очередь – это структура данных типа FIFO. Она хранит все функции в том порядке, в котором они были туда добавлены.

Очередь обратных вызовов (callback queue) обрабатывает цикл событий (event loop). Он смотрит на эту очередь и на стек вызовов (call stack). Если стек вызовов пуст, а очередь нет – то он берёт первую функцию из очереди и закидывает её в стек вызовов, в котором она уже выполняется. Вот таким образом происходит выполнения асинхронного кода в JavaScript.

Если функцию myFunc необходимо вызывать не один раз, а постоянно через каждые 3 секунды, то тогда вместо setTimeout следует использовать setInterval:

window.setInterval(myFunc, 3000);

Пример, с передачей функции аргументов:

function sayHello(name) {
  alert(`Привет, ${name}!`);
}
setTimeout(sayHello, 3000, 'Василий'); // Привет, Василий!

Пример, с использованием в setTimeout анонимной функции:

setTimeout(function (name) {
  alert(`Привет, ${name}!`);
}, 3000, 'Василий');

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

Если функция setTimeout по каким-то причинам не работает, то проверьте действительно ли вы передаёте ссылку на функцию, а неё результат:

function sayHello() {
  console.log('Привет!');
}

// передаём в setTimeout не ссылку на функцию sayHello, а результат её вызова
setTimeout(sayHello(), 3000);

Отмена таймаута (clearTimeout)

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

Синтаксис отмены таймаута:

const timeoutId = window.setTimeout(...);
window.clearTimeout(timeoutId);

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

Например:

// запустим таймер и получим его идентификатор, который будет храниться в timeoutId
const timeoutId = window.setTimeout(() => {
  // он выведет alert с контентом 'Сообщение' через 4 секунды
  alert('Сообщение');
}, 4000);

// остановим таймер с помощью метода clearTimeout (для этого необходимо в качестве параметра данному методу передать идентификатор таймера, хранящийся в timeoutId)
clearTimeout(timeoutId);
Принцип работы с таймерами в JavaScript

Например, создадим на странице 2 кнопки. При нажатии на первую кнопку на экране будем отображать количество секунд, прошедших с момента её нажатия. При нажатии на вторую кнопку будем останавливать выполнение данного процесса.

Счётчик: <span id="countTime"></span>
<br />
<a href="javascript:startCount()">3anycтить процесс</a>
<br />
<a href="javascript:stopCount()">Остановить процесс</a>

<script>
  // глобальная переменная, хранящая количество секунд, прошедших с момента нажатия ссылки
  var count=0;
  // глобальная переменная, хранящая идентификатор таймера
  var timer;
  //функция, выполняет следующее:
  //1 - выводит значения переменной count в элемент с id="clock"
  //2 - увеличивает значения переменной на 1
  //3 - запускает таймер, который вызовет функцию timeCount() через 1 секунду
  function timeCount() {
    document.getElementById("countTime").innerHTML = count.toString();
    count++;
    timer = window.setTimeout(function(){ timeCount() },1000);
  }
  //функция проверяет выражение !timer по правилу лжи, если оно истинно,
  //то вызывает функцию timeCount()
  function startCount() {
    if (!timer)
      timeCount();
  }
    //функция проверяет выражение timer по правилу лжи
	//Если оно истинно, то она вызывает метод clearTimeOut() для прекращения работы таймера
	//и присваивает переменной timer значение null
	function stopCount() {
      if (timer) {
        clearTimeout(timer);
        timer=null;
      }
    }
</script>
Методы JavaScript setTimeout и clearTimeout

Методы setInterval и clearInterval

Метод setInterval предназначен для вызова кода на языке JavaScript через указанные промежутки времени. Он в отличие от метода setTimeOut будет вызвать код до тех пор, пока Вы не остановите этот таймер.

window.setInterval(Параметр_1, Параметр_2);

Метод setInterval имеет два обязательных параметра:

  • 1 параметр представляет собой строку, содержащую код на языке JavaScript (например, вызов функции);
  • 2 параметр задаёт интервал времени в миллисекундах, через который данный код будет вызываться.

Для прекращения работы данного таймера предназначен метод clearInterval, которому в качестве параметра необходимо передать уникальный идентификатор (id) таймера. Этот идентификатор можно получить при установке таймера, т.е. его возвращает метод setInterval. Также таймер прекращает свою работу при закрытии окна.

//запустим таймер и получим его идентификатор, который будет храниться в переменной timer1
//данный таймер будет выводить сообщение через каждые 5 секунд
var timer1 = window.setInterval("alert('Сообщение');",5000);
//остановим работу таймера с помощью метода clearInterval().
//Для этого в качестве параметра данному методу передадим идентификатор таймера, хранящийся в переменной timer1.
clearInterval(timer1);

Например, создадим цифровые часы:

<p id="clock"></p>
<a href="javaScript:startClock()">3anycтить таймер</a>
<br />
<a href="javaScript:stopClock()">Остановить таймер</a>

<script>
  // глобальная переменная, хранящая идентификатор таймера
  var timer;
  //Функция для запуска таймера
  function startClock() {
    //если переменная timer содержит ложь по правилу лжи (т.е. таймер не запущен)
    if (!timer) {
      //запустить таймер, который будет вызвать функцию outClock() каждую секунду (1с = 1000мс)
      //идентификатор данного таймера сохраним в переменную timer (т.е. теперь переменная timer равна истине по правилу лжи).
      timer = window.setInterval("outClock()",1000);
    }
  }
  // функция для вывода даты и времени в элемент с id="clock"
  function outClock() {
    //переменная, хранящая текущую дату и время
    var nowDateTime = new Date();
    //Выводим строку, содержащую дату и время в элемент с id="clock"
    document.getElementById("clock").innerHTML = nowDateTime.toLocaleTimeString();
  }
  // функция для остановки таймера
  function stopClock() {
    //если переменная timer содержит истину по правилу лжи (т.е. таймер запущен)
    if(timer) {
      //прекращаем работу таймера
      window.clearInterval(timer);
      //присваиваем переменной timer значение null, чтобы таймер опять можно было запустить
      timer=null;
      //Выводим пустую строку в элемент с id="clock"
      document.getElementById("clock").innerHTML="";
    }
  }
</script>
Методы JavaScript setInterval и clearInterval