Рубріки: Теория

Планирование в JavaScript: как применять функции setTimeout и setInterval

Денис Бородовский

Метод setTimeout — это встроенная функция JavaScript, устанавливающая таймер обратного отсчета (в миллисекундах) для выполнения функции обратного вызова по завершении заданного времени.

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

Как реализовать такую задумку? Именно для решения подобных задач в JavaScript существуют два метода объекта window: setTimeout и setInterval. В этой статье мы рассмотрим их грамотное использование и практическую реализацию. 

Метод setTimeout

Метод setTimeout запускается только один раз за вызов, а значит после завершения выполнения функции setTimeout прекращает свою работу. 

Основной и наиболее часто используемый синтаксис этой функции:

setTimeout (функция обратного вызова, задержка в миллисекундах)

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

function greet () {
alert ('Welcome!');
}
setTimeout (greet, 2000) ; // приветствие появится через 2000 миллисекунд (2 секунды)

setTimeout () позволяет нам назначить столько аргументов, сколько необходимо для функции обратного вызова. Предположим, что мы хотим поприветствовать Джона — пользователя, вошедшего в нашу систему. Для этого нам необходимо просто добавить аргументы в конец списка параметров setTimeout:

setTimeout (функция обратного вызова, задержка в миллисекундах, arg1)

Посмотрим, как это реализовать:

const loggedInUser = 'Джон' ;
function greet (userName) {
alert ('Добро пожаловать' + userName + '!');
}
setTimeout (greet, 2000, loggedInUser);

В приведенном примере в появившемся через две секунды всплывающем окне будет написано «Добро пожаловать, Джон!». Здесь мы взяли имя пользователя из переменной loggedInUser и добавили его в функцию обратного вызова в качестве параметра.

Важно: не передавайте аргументы функции обратного вызова напрямую и не ставьте скобки после имени функции внутри метода setTimeout, если аргументов нет!

Глядя на код предыдущего примера у вас может возникнуть соблазн передать аргументы в качестве параметров функции обратного вызова. Например:

SetTimeout (Greet(loggedInUser), 2000);

или

SetTimeout (Greet(),2000) ;

Это логическая ошибка. В приведенной выше строке кода функция обратного вызова выполняется немедленно, а параметр задержки уже не воспринимается интерпретатором. Получается, что мы не передаем объект функции в качестве метода обратного вызова в setTimeout (), а скорее устанавливаем возвращаемое значение функции. В такой ситуации таймер будет проигнорирован.

Чтобы исправить эту ошибку, просто измените шаблон вызова, удалите скобки после имени функции, добавив аргументы (если они есть) в конец списка параметров setTimeout ()

Наши друзья из Mate Academy помогут всем заинтересованным разобраться в особенностях работы с setTimeout и setInterval. Запишитесь на специальный курс и получите ценные знания.

Функция clearTimeout

Иногда бывают ситуации, когда нам нужно отменить начавшуюся временную задержку. Что делать в этом случае? Метод setTimeout () возвращает ключ, позволяющий ссылаться на выполнение callback-функции. Мы можем использовать этот ключ как кнопку прерывания, тем самым взяв в свои руки контроль над самим механизмом тайм-аута. Например:

const timerId = setTimeout(Greet, 5000, loggedInUser);

Здесь timerId — тот самый ключ, используемый для ссылки на текущий таймер. Чтобы отменить тайм-аут, этот ключ нужно передать функции clearTimeout () в качестве параметра:

function _clear_ () {
clearTimeout (timerId);
}

При использовании clearTimeout() происходит отмена таймера, и функция обратного вызова внутри метода не сработает. 

Метод setInterval

Метод setInterval используется для повторного выполнения функции по истечении определенного периода времени. Схема построения этого метода следующая:

const timerID = setInterval(function, delay, arg1, arg2, ...)

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

Синтаксис этого метода очень похож на синтаксис setTimeout, но в отличие от последнего, setInterval запускает функцию несколько раз, пока она не будет отменена. Например:

function sayHello(name) {
    console.log(`Hello, ${name}`)
}

setInterval(sayHello, 3000, "John")

В примере метод будет повторно выводить в консоль Hello John каждые три секунды. 

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

Вернемся к нашему примеру:

let counter = 0;
function sayHello(name) {
    alert(`Hello, ${name}`)
    counter++;

    if (counter === 3) {
        clearInterval(timerID)
    }
}

let timerID = setInterval(sayHello, 3000, "John");

Функция sayHello будет выполнена всего три раза.

Вы можете задаться вопросом, почему оператор if находится внутри функции, а не за ее пределами, вот так:

let counter = 0;
function sayHello(name) {
    alert(`Hello, ${name}`)
    counter++;
}

let timerID = setInterval(sayHello, 3000, "John")

if (counter === 3) {
    clearInterval(timerID)
}

Для того чтобы ответить на него, необходимо разобраться, как JavaScript выполняет рассматриваемые нами методы. В отличие от других языков, в JS — один поток для решения задач, выполняемый построчно. Это значит, что каждая строка кода должна завершить свое выполнение, прежде чем переходить к следующей. Другими словами, выполнение остального кода блокируется.

Но есть операции ввода-вывода, которые не блокируются. Они обрабатываются базовым механизмом. К ним относятся:

  • получение данных через Ajax;
  • метод setTimeout;
  • метод setInterval.

Поэтому JavaScript не ждет, пока функция обратного вызова, переданная методу setTimeout или setInterval, завершит выполнение прежде, чем перейти к следующей задаче или строке кода.

Следовательно, если бы мы прописали реализацию метода вторым способом, таймер не перестал бы работать, ведь после выполнения строки let timerID = setInterval(sayHello, 3000, "John") JavaScript перешел бы к следующему блоку кода if (counter === 3) {clearInterval(timerID)} и условие не выполнилось бы. 

Рекурсивный setTimeout

Метод setInterval запускает функцию каждые n миллисекунд, не обращая внимания на то, когда она завершится.

Если функция каждый раз срабатывает с одним и тем же временным интервалом, тогда все в порядке:

Функция срабатывает с одним и тем же временным интервалом

Но бывают случаи, когда время выполнения меняется в зависимости от условий сети, например:

Время выполнения может меняться

Или перекрывает следующее:

Время выполнения перекрывает следующее за ним

Чтобы избежать последствий подобных случаев, вы можете запланировать рекурсивный вызов setTimeout по завершении функции обратного вызова.

Для наглядности рассмотрим распространенный пример сервиса по отправке запросов на сервер, который при перегрузке последнего увеличивал бы интервал следующего запроса:

let delay = 5000;
let timerId = setTimeout(function request() {
  ...функционал для отправки запроса...
  if ( сервер перегружен ) {
    // увеличиваем интервал последующего запроса
    delay *= 2;
  }
  timerId = setTimeout(request, delay);
}, delay);

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

Схема здесь такая:

Фиксированная временная задержка

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

Что надо помнить

На функцию из рассматриваемых методов создается внутренняя ссылка и прописывается в планировщик. Сохранившись там, она не попадет в Garbage Collector даже в том случае, когда на нее ничто не ссылается. Функцию же setInterval сотрет из памяти только метод clearInterval

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

Нулевая задержка

В официальной документации к setTimeout написано, что delay (временная задержка) — необязательное поле. Если оно не указано, то таймер равен нулю и функция обратного вызова срабатывает «мгновенно или как можно скорее». Такая странная формулировка обусловлена наличием у браузера своего таймера с минимально возможной задержкой. Она может варьироваться от 4 мс в современных браузерах, до 15 мс в более старых. Но срабатывать она может и реже — с опозданием вплоть до 1000 мс.

Это связано с особенностью некоторых браузеров запускать таймер на неактивной странице. Этим грешат Google Chrome, Firefox, IE10. На частоту запуска функции также влияют загрузка процессора и режим экономии энергии разряженной батареи при работе на ноутбуке. Узнать более подробно об остальных особенностях работы, можно записавшись на онлайн курс к нашим друзьям Mate Academy

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

Рассмотрим простой пример реализации всплывающего сообщения, появляющегося спустя 2000 миллисекунд после наведения мышкой на элемент. Условие: если курсор убрали раньше, сообщение не нужно показывать.

var timerId;
element.hoverover = function() { 
timerId = setTimeout(function() { 
alert(1); 
}, 2000); 
}; 
element.hoverout = function() {clearTimeout(timerId);};

Примечание: если методу clearTimeout передан неправильный идентификатор, то изменений не произойдет. Поэтому здесь можно не проверять работу таймера и содержимое timerId.

Резюмируем

  1. Методы-таймеры setInterval и setTimeout нужны для отложенного запуска необходимого нам функционала.
  2. setTimeoutсразу прекращает свою работу при запуске отложенной функции, а остановить его во время выполнения, может метод clearTimeout.
  3. setIntervalзаставляет callback-функцию выполняться с заданным интервалом. Для его остановки в JS предусмотрен метод clearInterval().
  4. В JavaScript существует также рекурсивный setTimeout, гарантирующий фиксированную временную задержку между вызовами функции.
  5. При неуказанном временном параметре в setTimeout задержка вызова функции зависит от браузерного таймера, загруженности центрального процессора и режима расходования энергии при работе на ноутбуке.

 

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023