- JS-движок умеет только синхронный код (call stack). Всё асинхронное — это Web API браузера, вне движка.
- Web API (таймеры, fetch, события, observers) по готовности кладут колбэк в очередь.
- Event loop: стек пуст → выгрести ВСЕ микротаски → взять 1 макротаску → (рендер если время).
- Микро (
Promise,queueMicrotask,MutationObserver) всегда раньше макро (setTimeout, события). await= микрозадача.setTimeout(0)≠ 0 мс (клампинг ~4 мс).- Долгий синхронный JS блокирует всё; бесконечные микротаски = render starvation.
Откуда асинхронность: движок ≠ браузер
Сам JS однопоточный и синхронный. Асинхронность даёт хост — браузер.
Call stack
Стек вызовов (LIFO), где движок (V8) исполняет синхронный код. Движок знает только язык — ни таймеров, ни DOM.
Web APIs
Таймеры, fetch, события, storage, observers. Предоставлены браузером, работают вне движка (часто на других потоках).
Очереди
Готовые колбэки ждут здесь: macrotask queue и microtask queue.
Event loop
Диспетчер: когда стек пуст — перекладывает колбэки из очередей в стек по строгим правилам.
Promise — это язык (ECMAScript). А setTimeout, fetch, localStorage, DOM — это Web API (их нет в Node, там свои). Отсюда и разница очередей.Колесо event loop (один tick)
Один оборот — и из него растут все задачки «что выведется».
Микрозадачи vs макрозадачи
Кто куда попадает — и почему порядок именно такой.
| 🟢 Микрозадачи (раньше) | 🟡 Макрозадачи (позже) |
|---|---|
Promise.then/catch/finallyпродолжение после awaitqueueMicrotask()MutationObserver |
setTimeout, setIntervalDOM-события (click, …) MessageChannel, postMessageсетевые/I-O колбэки |
requestAnimationFrame, IntersectionObserver, ResizeObserver — это не очередь задач, а часть шага рендера (перед paint). requestIdleCallback — в простое после кадра.Задачи «что выведется»
Самый частый формат на собесе. Разбор по шагам.
Базовая
console.log('1');
setTimeout(() => console.log('2')); // макро
Promise.resolve().then(() => console.log('3')); // микро
console.log('4');
// → 1, 4, 3, 2
Синхронный код (1, 4) → все микротаски (3) → следующая макрозадача (2).
С async/await
async function a(){ console.log('a1'); await b(); console.log('a2'); }
function b(){ console.log('b'); }
console.log('start'); a(); console.log('end');
// → start, a1, b, end, a2
await b() выполняет b() синхронно прямо сейчас. А весь код после await (a2) превращается в микрозадачу — поэтому он позже end.
async/await под капотом
Частый вопрос «что возвращает async-функция / что под капотом».
• async-функция всегда возвращает Promise (значение оборачивается в Promise.resolve).
• await x — синтаксический сахар над x.then(...): код после await = колбэк в .then = микрозадача.
• await ставит «на паузу» только текущую async-функцию, не весь поток.
• Ошибка в await ловится обычным try/catch (= .catch).
Почему setTimeout неточный
Задержка — это «не раньше чем», а не «ровно через».
| Факт | Деталь |
|---|---|
| Минимум не 0 | при вложенности > 5 браузер клампит до ~4 мс |
| Throttle на фоне | в неактивной вкладке таймеры тормозят (≥ 1000 мс) |
| Только «не раньше» | колбэк ждёт, пока стек освободится — занят поток, задержка больше |
setInterval дрейфит | накапливает погрешность; для точности — рекурсивный setTimeout или rAF |
var + setTimeout в цикле выведет последнее значение — колбэк сработает после цикла, а var одна на всех. Лечение: let (своя на итерацию).Браузерные API — обзор по категориям
Что предоставляет браузер сверх языка. Группы, которые спрашивают.
⏱ Таймеры / кадры
setTimeout, setInterval, requestAnimationFrame (в кадр), requestIdleCallback (в простое).
🌐 Сеть
fetch (промис), XMLHttpRequest (старый, на событиях), WebSocket (дуплекс), EventSource/SSE (сервер→клиент), sendBeacon (аналитика на выходе).
💾 Хранилища
localStorage, sessionStorage, cookie, IndexedDB (асинхр., объекты), Cache API.
👁 Наблюдатели
IntersectionObserver (виден ли — lazy/infinite scroll), MutationObserver (DOM-изменения, микро), ResizeObserver, PerformanceObserver.
🧵 Воркеры
Web Worker (тяжёлый JS без DOM), Service Worker (прокси сети, оффлайн, push), SharedWorker.
🖱 События / DOM
addEventListener (capture/bubble), CustomEvent, History (pushState), URL, FormData.
Кто в какую очередь кладёт колбэк
Главная сводка для задачек на порядок.
| API | Куда попадает колбэк |
|---|---|
Promise.then / await / queueMicrotask | 🟢 микрозадача |
MutationObserver | 🟢 микрозадача |
fetch().then(...) | 🟢 микро (после того как сеть — Web API — зарезолвит промис) |
setTimeout / setInterval | 🟡 макрозадача |
DOM-событие, MessageChannel | 🟡 макрозадача |
requestAnimationFrame | 🎞 шаг рендера (перед paint) |
IntersectionObserver / ResizeObserver | 🎞 шаг рендера |
requestIdleCallback | 💤 простой после кадра |
Хранилища: localStorage / sessionStorage / cookie / IndexedDB
Один из самых частых вопросов фронт-собеса (отличия и кейсы).
| Объём | Живёт | Шлётся на сервер | API | |
|---|---|---|---|---|
| localStorage | ~5–10 МБ | навсегда (до очистки) | нет | синхр., строки |
| sessionStorage | ~5 МБ | пока открыта вкладка | нет | синхр., строки |
| cookie | ~4 КБ | до expires | да, в каждом запросе | строка; флаги HttpOnly/Secure/SameSite |
| IndexedDB | сотни МБ+ | навсегда | нет | асинхр., объекты, индексы |
localStorage синхронный — большие операции блокируют поток; хранит только строки (объект → JSON.stringify); лимит ~5 МБ.Подводные камни
- «
setTimeout(0)= сразу» — нет, после стека и микротасок - Долгий синхронный код «подвиснет» — блокирует event loop
- Бесконечные
.then→ render starvation localStorage«асинхронный» — нет, синхронныйvarв цикле сsetTimeout→ последнее значение
- Точный порядок: sync → micro → (render) → macro
- Тяжёлое — в Web Worker / дробить задачи
- Не плодить микротаски рекурсивно
- Объекты в storage — через
JSON letв циклах с замыканиями
Реальные вопросы с собесов
Из нашей базы 2000+ собесов.
• Event Loop — порядок микро/макро, «что выведется» (промисы + setTimeout) — Sok Studio, Dinord, ОрниТех, Иннотех
• setTimeout/setInterval — отличия, какую задачу порождает — Онлайн Гимназия, Альфа-банк
• async/await — для чего, что возвращает, что под капотом — Домклик, JSA-group, ПОЛЕ.РФ, Айтиком
• localStorage / sessionStorage / cookie — отличия и кейсы — Сбер, Digital Sector, РСХБ-Интех
• Web Worker и Service Worker — что это, отличия — aQsi, Prooftech IT, F6
• IntersectionObserver / MutationObserver — что и как — Quantum Art, Ашан Тех, Сбер
• var + setTimeout в замыкании — что выведет — Queen Interactive
Каверзные follow-up
Граничит с темами
Покрыто здесь, но рядом — отдельные разборы:
all/race/allSettled/any, цепочки, ошибки) · DOM-события (фазы, делегирование) · Web Workers вглубь · Микрозадачи в Node (process.nextTick + фазы) — берём отдельно по запросу.