intermediate
Node event loop
Объясняйте event-loop phases, microtasks, nextTick, timers, immediates, I/O callbacks и starvation risks.
Node выполняет JavaScript в одном главном потоке и планирует асинхронную работу через libuv. Event loop — не одна очередь, а набор фаз, которые проходят по порядку на каждом витке:
┌───────────────────────────┐
│ timers │ колбэки setTimeout / setInterval
├───────────────────────────┤
│ pending callbacks │ I/O-колбэки, отложенные с прошлого витка
├───────────────────────────┤
│ idle, prepare │ внутренняя служебная работа libuv
├───────────────────────────┤
│ poll │ новые I/O-события; блокировка при простое
├───────────────────────────┤
│ check │ колбэки setImmediate
├───────────────────────────┤
│ close callbacks │ например socket.on('close')
└───────────────────────────┘
Между фазами и после витка выполняются microtasks: сначала `process.nextTick`, затем Promise jobs. Отсюда — почему злоупотребление `nextTick` может «голодать» I/O.
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
// nextTick → promise → (timeout или immediate зависит от контекста)
На интервью: назовите фазы, приоритет microtasks и связь блокировки main thread с задержками для всех одновременных запросов.
Типовые ошибки: называть Node «многопоточным» без thread pool; считать, что `setTimeout(fn, 0)` всегда раньше I/O; рекурсивный `nextTick`, блокирующий фазу poll.
Компромисс — между простотой, производительностью, безопасностью и эксплуатацией: назовите, что оптимизировали и какую цену приняли.
Чеклист:
- Нарисуйте фазы и место timers vs immediates.
- Объясните microtasks vs macrotasks в Node.
- Свяжите блокировку main thread с латентностью сервиса.
- Упомяните thread pool libuv для части I/O, не для всего JS.