Что такое event loop в JavaScript и как он работает с микрозадачами и макрозадачами
Что проверяют
Понимаете ли вы, почему однопоточный JavaScript не блокируется на асинхронных операциях, и знаете ли разницу в приоритете очередей. Это базовый вопрос, по ответу на него судят, разбираетесь ли вы в асинхронности вообще.
Как отвечать
Событийный цикл это механизм, который решает, какой код выполнять следующим. Работает он с несколькими сущностями:
- стек вызовов: синхронный код, выполняется здесь и сейчас;
- web API это то, что умеет браузер: таймеры, сеть, события DOM. Сам JavaScript их не выполняет, он передаёт задачу и идёт дальше;
- очередь макрозадач: колбэки
setTimeout,setInterval, обработчики событий, ответы сети; - очередь микрозадач: колбэки промисов (
then,catch,finally),queueMicrotask,MutationObserver.
Главное правило порядка: цикл берёт задачу только когда стек вызовов пуст, и микрозадачи выполняются раньше макрозадач, причём вся очередь микрозадач целиком, а не по одной. То есть после каждой макрозадачи движок разгребает микрозадачи до конца, и только потом берёт следующую макрозадачу.
Проверяется это классическим примером:
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// 1, 4, 3, 2setTimeout с нулевой задержкой выполнится последним, потому что это макрозадача, а промис попадает в очередь микрозадач.
Где спотыкаются
Говорят, что setTimeout(fn, 0) выполнится немедленно. Он выполнится не раньше, чем опустеет стек и разгребутся микрозадачи, а задержка 0 означает лишь «как можно скорее», а не «сейчас».
Второе: бесконечно порождающие себя микрозадачи вешают страницу намертво. Очередь микрозадач разгребается целиком, поэтому рендер до неё просто не доходит.
Третье: путают событийный цикл браузера и Node.js. В Node у цикла есть фазы (таймеры, poll, check), а process.nextTick идёт даже раньше промисов. Если позиция на Node.js, про фазы стоит сказать самому.