Кратко
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 секунд. Но метроном не знает, сколько времени занял каждый удар — он просто ждёт интервал.
Примеры
**Самый простой:**
setTimeout(() => console.log('1'), 0);
setTimeout(() => console.log('2'), 0);
console.log('3');
// Вывод: 3, 1, 2
// setTimeout(fn, 0) выполняется после синхронного кодаТаймеры с delay=0 выполняются после текущего синхронного кода.
**Простой:**
let count = 0;
const id = setInterval(() => {
count++;
console.log(`Счётчик: ${count}`);
if (count >= 5) clearInterval(id);
}, 1000);
// Вывод: 1, 2, 3, 4, 5 (через каждую секунду)setInterval + clearInterval для ограничения повторений.
**Реальный пример:**
// Рекурсивный 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) — промисы: лучший способ обрабатывать асинхронные результаты.