Микрозадачи и макрозадачи
Макрозадачи (Macrotasks)
Каждый «тик» event loop - это одна макрозадача. После неё браузер может отрисовать кадр.
Источники макрозадач: setTimeout, setInterval, setImmediate (Node.js), I/O-события, обработчики UI-событий.
Микрозадачи (Microtasks)
Выполняются сразу после текущей задачи (или текущего синхронного кода), до следующей макрозадачи - и до рендера.
Источники микрозадач: Promise.then/catch/finally, queueMicrotask(), MutationObserver.
Порядок выполнения
console.log("1 - sync")
setTimeout(() => console.log("2 - macro"), 0)
Promise.resolve()
.then(() => console.log("3 - micro"))
.then(() => console.log("4 - micro"))
queueMicrotask(() => console.log("5 - micro"))
console.log("6 - sync")
// Вывод: 1, 6, 3, 5, 4, 2
Почему 3, 5, 4 - все до 2?
Перед тем как взять следующую макрозадачу (setTimeout), event loop полностью опустошает очередь микрозадач. Причём если микрозадача порождает новую микрозадачу - та тоже выполняется до макрозадачи.
Важный кейс
// Бесконечная рекурсия микрозадач заморозит браузер!
function infinite() {
Promise.resolve().then(infinite)
}
infinite() // браузер не отрисует ни одного кадра