JS Async

Таймеры: setTimeout и setInterval

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

Средний (Intermediate)

Кратко

setTimeout(fn, delay) выполняет fn через delay миллисекунд. setInterval(fn, interval) повторяет fn каждые interval миллисекунд. clearTimeout/id clearTimeout отменяют таймеры. Важно: setInterval не компенсирует время выполнения callback.


Подробно

setTimeout: const id = setTimeout(() => { console.log('Через 1 секунду'); }, 1000);

clearTimeout(id); // отмена — callback не вызовется

setInterval: const intervalId = setInterval(() => { console.log('Каждую секунду'); }, 1000);

// Остановить через 5 секунд: setTimeout(() => clearInterval(intervalId), 5000);

Проблема setInterval: setInterval(() => { // операция занимает 300мс console.log(new Date().toISOString()); }, 1000);

// Интервал: 1000мс + 300мс выполнения = 1300мс между вызовами // Таймер «дрейфует»

Правильный способ — рекурсивный setTimeout: function repeat() { // операция setTimeout(repeat, 1000); // следующий вызов ПОСЛЕ завершения } setTimeout(repeat, 1000);

Минимальная задержка:

  • В браузере: минимум ~4мс (для вложенных таймеров)
  • setTimeout(fn, 0) ≈ 0-4мс
  • В фоновых вкладках: может быть увеличена до ~1000мс

Ментальная модель

setTimeout — как будильник: поставьте на конкретное время — он зазвонит один раз. setInterval — как метроном: повторяет звук каждые N секунд. Но метроном не знает, сколько времени занял каждый удар — он просто ждёт интервал.


Примеры

**Самый простой:**

javascript
setTimeout(() => console.log('1'), 0);
setTimeout(() => console.log('2'), 0);
console.log('3');
// Вывод: 3, 1, 2
// setTimeout(fn, 0) выполняется после синхронного кода

Таймеры с delay=0 выполняются после текущего синхронного кода.

**Простой:**

javascript
let count = 0;
const id = setInterval(() => {
  count++;
  console.log(`Счётчик: ${count}`);
  if (count >= 5) clearInterval(id);
}, 1000);
// Вывод: 1, 2, 3, 4, 5 (через каждую секунду)

setInterval + clearInterval для ограничения повторений.

**Реальный пример:**

javascript
// Рекурсивный setTimeout (без дрейфа)
function countdown(seconds) {
  if (seconds <= 0) {
    console.log('Время вышло!');
    return;
  }
  console.log(`Осталось: ${seconds}с`);
  setTimeout(() => countdown(seconds - 1), 1000);
}
countdown(5);
// Вывод: 5, 4, 3, 2, 1, Время вышло!

Рекурсивный setTimeout гарантирует точный интервал.


Частые ошибки

**Неправильно:** Использовать setInterval для точных интервалов

**Почему:** setInterval не учитывает время выполнения callback — интервал «дрейфует».

**Правильно:** Используйте рекурсивный setTimeout для точных интервалов.

**Неправильно:** Забывать clearInterval

**Почему:** setInterval работает бесконечно — утечка памяти и ненужная нагрузка.

**Правильно:** Всегда планируйте остановку setInterval.

**Неправильно:** Думать, что delay возвращает точное время

**Почему:** Браузер может увеличить задержку (фоновые вкладки, нагрузка CPU).

**Правильно:** setTimeout/setInterval — «не раньше», а не «точно в».


Важно запомнить

  • setTimeout(fn, delay) — одноразовый таймер
  • setInterval(fn, interval) — повторяющийся таймер
  • clearTimeout/clearInterval отменяют таймер
  • Рекурсивный setTimeout точнее setInterval
  • Минимальная задержка ~4мс в браузере

Связь

Назад: Вы понимаете callback-функции (JA2) — теперь вы применяете их вместе с таймерами.

Вперёд: Следующий урок (JA4) — промисы: лучший способ обрабатывать асинхронные результаты.

Материалы курса JS Async — PROlab Academy.