🪿 IT Птица · шпаргалка к собесу

Event Loop и браузерные API (Web APIs)

За 30 секунд
Движок ≠ браузерКолесо loopМикро vs макро «Что выведется»await под капотомsetTimeout неточный Web API обзорКто в какую очередьХранилища Подводные камниВопросы
1

Откуда асинхронность: движок ≠ браузер

Сам JS однопоточный и синхронный. Асинхронность даёт хост — браузер.

Call stack— async вызов →Web API — готово →Очередь— стек пуст →Event loopCall stack

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, там свои). Отсюда и разница очередей.
2

Колесо event loop (один tick)

Один оборот — и из него растут все задачки «что выведется».

Взять одну макрозадачувыполнить целиком, пока стек не опустеет (синхронный скрипт — это первая задача)
Выгрести ВСЕ микрозадачидо конца, включая порождённые по ходу — ДО рендера
Рендер — если пришло время кадраrAF → style → layout → paint → composite
↻ и снова: следующая макрозадача
Главное правило: после каждой макрозадачи микроочередь выгребается полностью. Поэтому микро всегда обгоняют следующую макро.
3

Микрозадачи vs макрозадачи

Кто куда попадает — и почему порядок именно такой.

🟢 Микрозадачи (раньше)🟡 Макрозадачи (позже)
Promise.then/catch/finally
продолжение после await
queueMicrotask()
MutationObserver
setTimeout, setInterval
DOM-события (click, …)
MessageChannel, postMessage
сетевые/I-O колбэки
Особняком: requestAnimationFrame, IntersectionObserver, ResizeObserver — это не очередь задач, а часть шага рендера (перед paint). requestIdleCallback — в простое после кадра.
4

Задачи «что выведется»

Самый частый формат на собесе. Разбор по шагам.

Базовая

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.

5

async/await под капотом

Частый вопрос «что возвращает async-функция / что под капотом».

async-функция всегда возвращает Promise (значение оборачивается в Promise.resolve).
await x — синтаксический сахар над x.then(...): код после await = колбэк в .then = микрозадача.
await ставит «на паузу» только текущую async-функцию, не весь поток.
• Ошибка в await ловится обычным try/catch (= .catch).

Поэтому «await как будто синхронный» — иллюзия для читаемости. Под капотом это промисы и микротаски.
6

Почему setTimeout неточный

Задержка — это «не раньше чем», а не «ровно через».

ФактДеталь
Минимум не 0при вложенности > 5 браузер клампит до ~4 мс
Throttle на фонев неактивной вкладке таймеры тормозят (≥ 1000 мс)
Только «не раньше»колбэк ждёт, пока стек освободится — занят поток, задержка больше
setInterval дрейфитнакапливает погрешность; для точности — рекурсивный setTimeout или rAF
Классика-ловушка: var + setTimeout в цикле выведет последнее значение — колбэк сработает после цикла, а var одна на всех. Лечение: let (своя на итерацию).
7

Браузерные 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.

Web Worker vs Service Worker (частый вопрос): Web Worker — параллельные вычисления для одной страницы; Service Worker — фоновый прокси между страницей и сетью (кэш, оффлайн, push), живёт без открытой вкладки.
8

Кто в какую очередь кладёт колбэк

Главная сводка для задачек на порядок.

APIКуда попадает колбэк
Promise.then / await / queueMicrotask🟢 микрозадача
MutationObserver🟢 микрозадача
fetch().then(...)🟢 микро (после того как сеть — Web API — зарезолвит промис)
setTimeout / setInterval🟡 макрозадача
DOM-событие, MessageChannel🟡 макрозадача
requestAnimationFrame🎞 шаг рендера (перед paint)
IntersectionObserver / ResizeObserver🎞 шаг рендера
requestIdleCallback💤 простой после кадра
9

Хранилища: localStorage / sessionStorage / cookie / IndexedDB

Один из самых частых вопросов фронт-собеса (отличия и кейсы).

ОбъёмЖивётШлётся на серверAPI
localStorage~5–10 МБнавсегда (до очистки)нетсинхр., строки
sessionStorage~5 МБпока открыта вкладканетсинхр., строки
cookie~4 КБдо expiresда, в каждом запросестрока; флаги HttpOnly/Secure/SameSite
IndexedDBсотни МБ+навсегданетасинхр., объекты, индексы
Кейсы: токен сессии → cookie (HttpOnly, чтобы JS не достал); настройки UI/тема → localStorage; данные одной вкладки → sessionStorage; оффлайн/много структурированных данных → IndexedDB.
localStorage синхронный — большие операции блокируют поток; хранит только строки (объект → JSON.stringify); лимит ~5 МБ.
10

Подводные камни

❌ Частые ошибки
  • «setTimeout(0) = сразу» — нет, после стека и микротасок
  • Долгий синхронный код «подвиснет» — блокирует event loop
  • Бесконечные .then → render starvation
  • localStorage «асинхронный» — нет, синхронный
  • var в цикле с setTimeout → последнее значение
✅ Как правильно
  • Точный порядок: sync → micro → (render) → macro
  • Тяжёлое — в Web Worker / дробить задачи
  • Не плодить микротаски рекурсивно
  • Объекты в storage — через JSON
  • let в циклах с замыканиями
11

Реальные вопросы с собесов

Из нашей базы 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

await блокирует весь поток?
Нет — только свою async-функцию; остальной код идёт.
Что возвращает async-функция?
Всегда Promise.
Promise — это Web API?
Нет, это язык (ES). setTimeout/fetch — Web API.
MutationObserver — микро или макро?
Микрозадача.
Куда сложить токен — localStorage?
Лучше cookie HttpOnly (JS не достанет, защита от XSS-кражи).
requestAnimationFrame — это макрозадача?
Нет, часть шага рендера, перед paint.
12

Граничит с темами

Покрыто здесь, но рядом — отдельные разборы:

Промисы вглубь (методы all/race/allSettled/any, цепочки, ошибки) · DOM-события (фазы, делегирование) · Web Workers вглубь · Микрозадачи в Node (process.nextTick + фазы) — берём отдельно по запросу.