April 2, 2020

Планирование вызова функции: setTimeout

Представь, что у тебя есть задача вывести приветственное сообщение пользователю через 5 секунд после того, как он перешел на страницу. Как это организовать средствами JavaScript?

В этом вопросе нам поможет метод объекта windowsetTimeout(). Этот метод запускает через указанное в аргументах время, указанную в аргументах функцию. Пример использования:

// 1-ый вариант использования
window.setTimeout(func, delay);

// 2-ой вариант использования
setTimeout(func, delay);

Как видишь, данный метод объекта window можно вызывать и как обычную функцию (2-ой вариант использования). Я, кстати, рекомендую использовать именно 2-ой вариант, так как он чаще всего встречается и используется.

В качестве аргументов мы передаем func и delay, где func – это функция, либо ссылка на функцию, которую должен запустить setTimeout после того, как пройдет время delay (указывается в миллисекундах). Кстати, такие функции, которые передаются в другие функции в качестве аргументов – называются callback-функциями или функциями обратного вызова.

Теперь вернемся к нашей задаче поставленной в начале статьи и поприветствуем пользователя:

function hello() {
   alert('Привет!');
}

setTimeout(hello, 5000);

Функция hello, в данном примере, является callback-функцией, которую нужно запустить после того как пройдет 5000 миллисекунд (5 секунд). В данном примере, в качестве первого аргумента, мы передали ссылку на функцию hello. Ссылка на функцию – это ее имя, а не вызов. Т.е.:

hello; // - это ссылка на функцию
hello(); // - это вызов функции

В качестве аргумента мы должны передавать либо ссылку на функцию, либо описание функции(смотри ниже), но никогда не должны передавать вызов функции!

Есть и другие варианты передачи callback-функции, которые делают ровно тоже самое.

В качестве первого аргумента, мы можем передать анонимную(безымянную) функцию:

setTimeout(function() { alert('Привет!'); }, 5000);

Так же можем передать анонимную стрелочную функцию:

setTimeout(() => alert('Привет!'), 5000);

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

Продолжим.

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

setTimeout(`alert('Привет!')`, 5000);

Все варианты выполняют одну и ту же задачу, но, последний вариант с передачей строки:

setTimeout(`alert('Привет!')`, 5000);

использовать не рекомендуется, так как это и стилистически не красиво и в плане безопасности – это плохой вариант. Стоит запомнить, что такой вариант может встретиться в чужом коде, но в своем такое писать не стоит.

Последнее что хотелось бы отметить, так это то, что метод setTimeout нужно удалять, чтобы не произошло утечки памяти. Делается это достаточно просто.

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

Чтобы не произошло утечки памяти, нужно удалять setTimeout после выполнения. Сделать этом можно с помощью clearTimeout(id), где в качестве аргумента нужно передать идентификатор вашего setTimeout.

Для начала давай сохраним идентификатор нашего setTimeout в константе timerId:

function hello() {
   alert('Привет');
}

const timerId = setTimeout(hello, 5000); 

Удалить setTimeout из памяти нам нужно после выполнения приветствия, поэтому добавим clearInterval в функцию hello, после вывода приветствия:

function hello() {
   alert('Привет');
   clearInterval(timerId);
}

const timerId = setTimeout(hello, 5000); 

Все, теперь после выполнения приветствия setTimeout будет удален из памяти.

Еще один небольшой пример:

function hello() {
   alert('Привет');
}

const timerId = setTimeout(hello, 5000);
clearInterval(timerId);

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