Язык JavaScript (ES5) → Асинхронное программирование → Планирование задач
Основная идея
setTimeout и setInterval — встроенные функции JavaScript для планирования выполнения кода через определённый промежуток времени. setTimeout выполняет код один раз, setInterval — многократно с заданным интервалом.
Ключевые аспекты
setTimeout(fn, delay) — вызывает функцию один раз через delay миллисекунд
setInterval(fn, delay) — вызывает функцию повторно каждые delay миллисекунд
Возвращаемое значение — числовой идентификатор таймера для отмены
Минимальная задержка — браузеры ограничивают минимум ~4мс для вложенных таймеров
Не гарантированная точность — задержка может быть больше из-за загруженности Event Loop
Плюсы
Простой и понятный API
Встроены в браузер и Node.js
Позволяют создавать анимации и периодические задачи
Не блокируют основной поток выполнения
Минусы
Неточное время срабатывания при высокой нагрузке
setInterval может накапливать вызовы при долгих операциях
Забытые интервалы приводят к утечкам памяти
Не работают в фоновых вкладках браузера как ожидается
Частые ошибки на собеседованиях
Путают порядок аргументов: сначала функция, потом задержка
Забывают сохранить ID таймера для последующей отмены
Не понимают, что задержка 0 не означает мгновенное выполнение
Используют setInterval для анимаций вместо requestAnimationFrame
Введение и проблематика
JavaScript — однопоточный язык, но веб-приложениям часто требуется выполнять действия с задержкой или периодически: показать уведомление через несколько секунд, обновлять данные каждую минуту, создавать анимации.
Для решения этих задач в JavaScript существуют функции планирования — setTimeout и setInterval. Они позволяют отложить выполнение кода или запускать его периодически, не блокируя основной поток.
setTimeout и setInterval не являются частью спецификации ECMAScript. Это Web API, предоставляемые браузером или средой выполнения (Node.js).
Базовая теория
setTimeout — отложенный вызов
setTimeout планирует однократное выполнение функции через указанное количество миллисекунд.
Code Example 2: Что делает этот код? Как его остановить?
js
// Выводим текущее время каждую секундуconsttimerId=setInterval(() => {constnow=newDate();console.log(now.toLocaleTimeString());},1000);
Практические примеры
Отмена таймеров
Обе функции возвращают числовой идентификатор, который можно использовать для отмены:
js
// Отмена setTimeoutconsttimeoutId=setTimeout(() => {console.log('Это сообщение не появится');},5000);clearTimeout(timeoutId); // Отменяем до срабатывания
❓
Code Example 3: Сколько раз выведется сообщение "Счётчик: N"? Почему интервал остановится?
Всегда сохраняйте ID таймера, если планируете его отменять. Забытые интервалы — частая причина утечек памяти.
Вложенный setTimeout vs setInterval
Для периодических задач можно использовать два подхода:
❓
Code Example 4: В чём отличие между этими двумя подходами? Когда лучше использовать каждый?
js
// setInterval — фиксированный интервал между НАЧАЛОМ вызововsetInterval(() => {// Если операция занимает 100мс,// а интервал 1000мс,// следующий вызов будет через 900мс после завершенияdoSomething();},1000);
Важное отличие: вложенный setTimeout гарантирует паузу между выполнениями, а setInterval — нет. Если функция выполняется дольше интервала, вызовы setInterval могут накапливаться.
js
// Проблема с setInterval при долгих операцияхsetInterval(() => {// Если эта операция занимает 1500мс,// а интервал 1000мс — вызовы начнут "накапливаться"heavyOperation();},1000);// Решение — вложенный setTimeoutfunctionsafeInterval() {heavyOperation();setTimeout(safeInterval,1000); // Ждём 1с ПОСЛЕ завершения}setTimeout(safeInterval,1000);
Пограничные кейсы
Нулевая задержка
setTimeout(fn, 0) не выполняет функцию мгновенно. Код ставится в очередь макрозадач и выполняется после текущего синхронного кода:
❓
Code Example 5: В каком порядке выведутся сообщения? Почему?
js
console.log('1: Начало');setTimeout(() => {console.log('3: setTimeout с задержкой 0');},0);console.log('2: Конец');// Вывод:// 1: Начало// 2: Конец// 3: setTimeout с задержкой 0
Минимальная задержка в браузерах составляет около 4мс для вложенных таймеров (более 5 уровней вложенности). Это ограничение HTML5-спецификации.
Потеря контекста this
❓
Code Example 6: Какая проблема в первом вызове setTimeout? Как её решить?
js
constuser= { name:'Иван',sayHi() {console.log(`Привет, ${this.name}!`); }};// Ошибка — this будет undefined или windowsetTimeout(user.sayHi,1000); // "Привет, undefined!"// Решение 1: стрелочная функцияsetTimeout(() =>user.sayHi(),1000);// Решение 2: bindsetTimeout(user.sayHi.bind(user),1000);
Таймеры в фоновых вкладках
Браузеры ограничивают частоту таймеров в неактивных вкладках для экономии ресурсов:
js
// В фоновой вкладке интервал может быть принудительно// увеличен до 1000мс и болееsetInterval(() => {// Этот код может выполняться реже, чем ожидаетсяupdateUI();},100);
Плюсы и минусы
Аспект
setTimeout
setInterval
Использование
Однократные задачи
Повторяющиеся задачи
Контроль
Легко отменить
Нужно помнить об отмене
Накопление вызовов
Не проблема
Может накапливаться
Гибкость
Высокая (динамическая задержка)
Фиксированный интервал
Вопросы интервьюера
Q: Чем setTimeout с задержкой 0 отличается от немедленного вызова функции?
setTimeout(fn, 0) откладывает выполнение до следующей итерации Event Loop. Это позволяет браузеру обновить UI и обработать другие события перед выполнением функции.
Q: Почему setInterval может быть неточным?
Браузер не гарантирует точное время. Если Event Loop занят, вызов откладывается. Также браузеры ограничивают частоту в фоновых вкладках.
Q: Как реализовать точный интервал для анимаций?
Для анимаций лучше использовать requestAnimationFrame — он синхронизирован с частотой обновления экрана (обычно 60fps).
Q: Можно ли передать строку вместо функции в setTimeout?
Технически да (setTimeout("alert('hi')", 100)), но это плохая практика — работает как eval() и создаёт уязвимости безопасности.