Skip to main content

setTimeout і setInterval в JavaScript

setTimeout планує виконання колбека один раз після вказаної затримки; setInterval викликає його повторно з кожним тиком інтервалу, поки ти його не зупиниш.

Теорія

TL;DR

  • setTimeout спрацьовує один раз, як таймер для однієї піци. setInterval дзвонить постійно, як будильник кожні 5 хвилин, поки ти його не вимкнеш.
  • Головна різниця: одноразовий проти повторюваного.
  • Потрібно виконати щось один раз? setTimeout. Потрібен цикл? setInterval, але для асинхронних задач краще ланцюжок setTimeout.
  • Обидва повертають ID для clearTimeout або clearInterval.
  • Затримка - це мінімум, не гарантія. Event loop може додати більше часу.

Швидкий приклад

javascript
// setTimeout: спрацьовує ОДИН РАЗ після затримки const timeoutId = setTimeout(() => console.log("Спрацювало один раз"), 100); // clearTimeout(timeoutId); // скасувати до спрацювання // setInterval: спрацьовує ПОВТОРНО кожен інтервал const intervalId = setInterval(() => console.log("Тік"), 1000); setTimeout(() => clearInterval(intervalId), 5000); // зупинити після ~5 тиків

setTimeout додає одну задачу в чергу. setInterval додає нову задачу кожен інтервал, незалежно від того, чи завершилася попередня.

Ключова різниця

setInterval планує наступний виклик одразу, як тільки минає інтервал. Він не чекає, поки колбек завершиться. Якщо колбек виконується 1.5 секунди, а інтервал - 1 секунда, виклики починають накопичуватися в черзі. setTimeout спрацьовує один раз і зупиняється. Якщо потрібен повторюваний таймер, який чекає завершення колбека, роби ланцюжок setTimeout всередині самого себе.

Коли що використовувати

  • Приховати спінер після затримки: setTimeout.
  • Опитувати API кожні 5 секунд: setInterval для простих випадків, або ланцюжок setTimeout коли колбек асинхронний.
  • Дебаунс поля пошуку: ланцюжок setTimeout з clearTimeout на кожне натискання клавіші.
  • Плавна анімація 60 кадрів/с: requestAnimationFrame, не таймери.
  • Уникнути блокування UI: будь-який таймер краще за цикл while.

Таблиця порівняння

ВластивістьsetTimeoutsetInterval
ВиконанняОдин раз після затримкиПовторно кожен інтервал
Мінімальна затримка~4мс (браузери обмежують вкладені виклики)~4мс, але виклики можуть накладатись
СкасуванняclearTimeout(id)clearInterval(id)
Безпека з asyncПередбачуваноМоже накопичуватись, якщо колбек повільний
Коли використовуватиДебаунс, одноразові затримкиЛічильники, просте опитування

Як браузер це обробляє

setTimeout і setInterval - це не частина JavaScript-рушія. Вони живуть у шарі Web API браузера (або libuv у Node.js). Коли затримка закінчується, браузер кладе колбек у чергу макротасків. Event loop підхоплює його тільки після того, як стек викликів і всі мікротаски (Promise) очищені. Саме тому setTimeout(fn, 0) не запускається миттєво. Нуль мілісекунд - це не нуль часу.

Браузери також встановлюють мінімум ~4мс для вкладених викликів setTimeout після 5 рівнів вкладеності (специфікація HTML5). У фонових вкладках таймери можуть гальмуватися до 1000мс.

Типові помилки

Передача виклику функції замість посилання

javascript
function greet(name) { console.log(`Привіт ${name}`); } setTimeout(greet("Аліса"), 1000); // виконується одразу, передає undefined setTimeout(() => greet("Аліса"), 1000); // правильно

greet("Аліса") виконується негайно і повертає undefined. Ти передаєш цей результат у setTimeout, а не саму функцію.

Забутий cleanup у React компонентах

javascript
// Витік пам'яті: інтервал продовжує жити після демонтування useEffect(() => { setInterval(() => fetchData(), 5000); }, []); // Правильно useEffect(() => { const id = setInterval(fetchData, 5000); return () => clearInterval(id); }, []);

Інтервал продовжує працювати після демонтування компонента. Виправлення - один рядок: повернути функцію cleanup.

setInterval з повільними асинхронними колбеками

javascript
// Виклики можуть накластись, якщо fetch займає більше 5с setInterval(async () => { const data = await fetch("/api/status"); updateUI(data); }, 5000); // Краще: чекати завершення кожного виклику function poll() { fetch("/api/status") .then(res => res.json()) .then(data => { updateUI(data); setTimeout(poll, 5000); }); } poll();

Спроба змінити інтервал зсередини колбека

javascript
let delay = 1000; setInterval(() => { delay *= 2; // нічого не змінює - інтервал зафіксований при плануванні }, delay); // Якщо потрібні динамічні затримки - використовуй ланцюжок setTimeout: let delay = 1000; function tick() { console.log("Тік"); delay *= 2; setTimeout(tick, delay); } setTimeout(tick, delay);

Де зустрічається у продакшені

  • React поле пошуку: useEffect + setTimeout для дебаунсу 300мс, clearTimeout у cleanup.
  • Redux DevTools: setInterval для періодичних знімків стану.
  • VS Code: ланцюжки setTimeout для дебаунсу автодоповнення при введенні.
  • Express/Node.js: таймери для коректного завершення сервера, setTimeout для примусового закриття після вікна cleanup.
  • Для анімацій: requestAnimationFrame замість setInterval. Для реал-тайм даних: WebSocket замість опитування.

Питання на співбесіді

Q: Чому setTimeout(fn, 0) не виконується миттєво?
A: Колбек потрапляє в чергу макротасків. Event loop спочатку виконує весь синхронний код і мікротаски, і лише потім підхоплює його. Нульова затримка означає «якнайшвидше після поточної роботи», а не «прямо зараз».

Q: Що таке обмеження ~4мс?
A: Специфікація HTML5 встановлює мінімум 4мс для вкладених викликів setTimeout після 5 рівнів вкладеності. У фонових вкладках браузери можуть гальмувати таймери до 1000мс і більше для економії CPU.

Q: Коли варто використовувати ланцюжок setTimeout замість setInterval?
A: Коли колбек асинхронний або займає непередбачуваний час. Ланцюжок гарантує, що наступний виклик починається тільки після завершення попереднього, тому виклики не накладаються.

Q: Як безпечно скасувати setInterval, якщо ID перехоплений у замиканні (closure)?
A: Зберігай ID у змінній поза колбеком: const id = setInterval(...); setTimeout(() => clearInterval(id), 5000). Не покладайся на те, що колбек сам збереже свій ID.

Q: У вкладці, де задачі займають 100мс, що станеться з setInterval(..., 10)?
A: Колбеки накопичуються у черзі, але не виконуються паралельно - JS однопоточний. Event loop обробляє їх по одному, тому реальні інтервали будуть близько 100мс, а не 10мс. Результат - черга, а не паралелізм.

Приклади

Базовий: відкладена одноразова дія

javascript
console.log("Старт"); const id = setTimeout(() => console.log("Спрацювало"), 100); console.log("Кінець"); // Вивід: Старт -> Кінець -> Спрацювало // "Кінець" виводиться до "Спрацювало", бо setTimeout асинхронний

setTimeout не блокує. Движок одразу переходить до console.log("Кінець") і виконує колбек пізніше з черги макротасків.

Середній: дебаунс (debounce) поля пошуку в React

javascript
function SearchInput() { const [query, setQuery] = useState(""); useEffect(() => { const id = setTimeout(() => { if (query) fetchResults(query); }, 300); return () => clearTimeout(id); // скасувати, якщо користувач продовжує друкувати }, [query]); return <input onChange={e => setQuery(e.target.value)} />; } // Кожне натискання клавіші скидає таймер. // API-запит відправляється тільки після 300мс тиші.

Коли query змінюється, старий таймер скасовується і стартує новий. Запит іде тільки тоді, коли користувач зупинився.

Просунутий: ланцюжок setTimeout проти setInterval для опитування API

javascript
// Версія з setInterval - може накладатись, якщо fetch повільний const id = setInterval(async () => { const res = await fetch("/api/health"); const data = await res.json(); console.log(data.status); }, 2000); // Ланцюжок setTimeout - чекає кожної відповіді function pollHealth() { fetch("/api/health") .then(r => r.json()) .then(data => { console.log(data.status); setTimeout(pollHealth, 2000); // наступний запит після завершення цього }) .catch(() => setTimeout(pollHealth, 5000)); // відступ при помилці } pollHealth();

Ланцюжковий варіант дозволяє адаптувати затримку за результатом і чисто обробляти помилки. Варіант з setInterval може спричинити подвійні запити у продакшені, коли сервери починають гальмувати під навантаженням: один виклик ще не завершився, а наступний вже стартував.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?