Таймери та слухачі подій
1. Що роблять таймери та слухачі подій
setTimeout,setInterval,addEventListenerстворюють асинхронні задачі, які живуть окремо від основного стека виконання JS.- Ці задачі утримують посилання на все, що використовується всередині колбеків (через замикання).
Поки таймер активний або слухач "підписаний", усі об'єкти, до яких звертається його callback, вважаються досяжними → GC не може їх видалити.
2. Чому важливо очищати
Без очищення - об'єкти залишаються в пам'яті назавжди
javascript
function startTimer() {
const bigData = new Array(1e6).fill('data');
setInterval(() => console.log(bigData.length), 1000);
}
startTimer();Тут bigData ніколи не звільняється:
- інтервал живе вічно;
- замикання утримує посилання на
bigData; - GC вважає
bigData"живим" → пам'ять росте.
Потрібно очищати:
javascript
const id = setInterval(...);
clearInterval(id);Слухачі подій - та сама історія
javascript
const button = document.getElementById('btn');
button.addEventListener('click', () => console.log('clicked'));
button.remove(); // елемент видалено, слухач залишивсяТепер:
- елемент
buttonвидалено з DOM, але callback все ще зберігається в пам'яті (у JS залишилось посилання на нього); - GC не може видалити об'єкт, тому що на нього "дивиться" зареєстрований обробник.
Рішення:
javascript
button.removeEventListener('click', handler);3. Чому це особливо критично в SPA-застосунках
У SPA сторінка не перезавантажується. Все, що не очищається, живе, поки користувач не закриє вкладку.
Наприклад:
- ти переходиш між сторінками;
- компоненти монтуються й демонтуються;
- якщо таймери й слухачі не прибираються, кожен "старий" компонент продовжує існувати в пам'яті → накопичується сміття.
Через 30 хвилин роботи React-застосунку можна побачити:
- сотні активних інтервалів,
- десятки "зомбі"-слухачів,
- heap memory росте без повернення.
4. Як саме це утримує пам'ять (механізм)
| Об'єкт | Посилання | Чому не очищається |
|---|---|---|
| Таймер | колбек (closure) | утримує посилання на всі змінні замикання |
| Слухач | callback + DOM-елемент | живуть, поки не викликати removeEventListener |
| WebSocket, RxJS, setInterval | активна підписка | утримує весь контекст |
| React useEffect | без cleanup | ефекти не скасовуються, посилання зберігаються |
GC вважає все це досяжним, тому що у нього є активні callback-посилання.
5. Як правильно очищати
Таймери
javascript
useEffect(() => {
const id = setInterval(() => {...}, 1000);
return () => clearInterval(id);
}, []);Слухачі подій
javascript
useEffect(() => {
const handler = () => console.log('scroll');
window.addEventListener('scroll', handler);
return () => window.removeEventListener('scroll', handler);
}, []);Підписки (RxJS, WebSocket, custom events)
javascript
useEffect(() => {
const sub = stream$.subscribe(...);
return () => sub.unsubscribe();
}, []);6. Що відбувається, якщо не чистити
| Наслідок | Що відбувається |
|---|---|
| Ріст пам'яті | callback-замикання утримують великі об'єкти |
| Лаги UI | event loop забитий активними callback-ами |
| Дублюючі виклики | старі слухачі спрацьовують разом з новими |
| Ріст CPU | забагато активних інтервалів |
| "Out of memory" | браузер або вкладка падає |
| "Зомбі-компоненти" | старі React-компоненти продовжують жити й виконувати ефекти |
7. Інструменти для виявлення
- Chrome DevTools → Memory → Heap snapshot
→ шукаємо "Detached DOM nodes" та об'єкти
Timeout,EventListener. - Performance → Record → "Long tasks".
- React DevTools Profiler → перевіряємо, що компоненти демонтуються.
- Sentry Performance → ріст heap, витоки, деградація FPS.
8. Резюме
| Що потрібно пам'ятати | Чому |
|---|---|
setTimeout / setInterval потрібно очищати | інакше callback залишається живим |
addEventListener потрібно знімати | інакше DOM і функція не звільняються |
У React - завжди return () => cleanup() у useEffect | запобігає "зомбі"-ефектам |
| У SPA немає "перезапуску" пам'яті | все накопичується, поки не очистиш вручну |
| Витоки пам'яті = лаги + падіння | GC не може видалити "живі" посилання |
Головна думка:
Таймер або слухач - це як відкрита вкладка в пам'яті. Якщо не закрити - таких вкладок стає сотні, і браузер "вмирає".
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.