Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Вкладені таймери». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Багато «вкладених» `setTimeout` (коли в колбеку ставиться наступний таймер) не переповнюють стек, але можуть забити чергу задач, спричинити дрейф часу, троттлінг і лаг інтерфейсу. **Ключове:** стек не росте, оскільки кожен `setTimeout` ставить наступну задачу в чергу макрозадач і завершує поточну.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)ЗображенняКоротко: **багато "вкладених"** `setTimeout` **(коли в колбеку ставиться наступний таймер)** не переповнюють стек, але можуть **забити чергу задач**, спричинити **дрейф часу**, **троттлінг** і **лаг інтерфейсу**. ## Що саме відбувається 1. **Немає stack overflow.** Кожен `setTimeout` ставить *наступну* задачу в чергу макрозадач і завершує поточну - стек очищується між викликами. 2. **Черга макрозадач росте.** Якщо всередині кожного колбека ставити кілька нових таймерів або виконувати важку роботу, задачі почнуть **накопичуватися**, а UI - підгальмовувати. 3. **Дрейф (накопичення затримки).** Час "цокання" зміщується: колбек виконається **не раніше** зазначеної затримки і ще **пізніше**, якщо стек був зайнятий. При довгих ланцюжках зміщення накопичується. 4. **Мінімальна затримка і троттлінг.** Навіть при `0` мс браузери застосовують **мінімальний інтервал** (зазвичай ≈4 мс; у неактивній вкладці - до ~1000 мс і більше). Вкладені таймери з надмалими затримками все одно будуть "зажиматися". 5. **Мікрозадачі між таймерами.** Після кожного колбека виконуються **всі мікрозадачі** (`Promise.then`, `queueMicrotask`). Якщо ви всередині таймера створюєте багато мікрозадач, вони будуть додатково подовжувати кроки ланцюжка. 6. **Порядок виконання.** Таймери беруться з черги за часом готовності; при "одночасній готовності" порядок може відрізнятися між рушіями, тому на конкретний порядок краще **не покладатися**. 7. **Пам'ять.** Якщо колбеки замикають великі об'єкти, а ланцюжок довгий/нескінченний, можливе **зростання споживання пам'яті** (об'єкти довше залишаються досяжними). ## Приклад "вкладеного" ланцюжка ```javascript function tick() { // робота... setTimeout(tick, 0); // наступний тік - у наступному обороті Event Loop } setTimeout(tick, 0); ``` - Стек не росте, але "тіки" будуть йти **з обмеженням мінімальної затримки** і з **дрейфом**. ## Як робити краще - **Компенсуйте дрейф** для періодичних задач: ```javascript const start = performance.now(); const step = 1000; // 1 сек let n = 0; function loop() { n++; // ...корисна робота... const nextAt = start + n * step; setTimeout(loop, Math.max(0, nextAt - performance.now())); } setTimeout(loop, step); ``` - Для анімацій використовуйте `requestAnimationFrame` - він синхронізований з перемальовуванням і менше "дрейфує". - Для "кожні N мс" без накладень надавайте перевагу **рекурсивному** `setTimeout` (як вище), а не `setInterval` - так ви уникнете накопичення, якщо колбек іноді довший за інтервал. - Важку роботу виносьте у **Web Worker**, щоб не блокувати UI. ## Висновок Вкладені `setTimeout` - безпечний спосіб "розірвати" синхронну рекурсію, але: - точного тайминга **не буде**, - при великому обсязі задач ви отримаєте **лаг і дрейф**, - браузер введе **мінімальні затримки** і троттлінг.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.