← Назад к вопросам
Как распределяются по очереди задач синхронный код?
2.0 Middle🔥 171 комментариев
#Soft Skills и рабочие процессы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как распределяются по очереди задач синхронный код
Этот вопрос касается Event Loop в JavaScript — механизма, который управляет выполнением кода, обработкой асинхронных операций и отрисовкой интерфейса. Понимание Event Loop критично для оптимизации производительности.
Call Stack и Event Loop
JavaScript — однопоточный язык. Все операции выполняются в единственном потоке через Call Stack:
function a() {
console.log('a');
b();
console.log('a завершена');
}
function b() {
console.log('b');
}
a();
// Вывод:
// a
// b
// a завершена
Порядок выполнения:
a()добавляется в стек- Выполняется
console.log('a') b()добавляется в стек- Выполняется
console.log('b') b()удаляется из стека- Выполняется
console.log('a завершена') a()удаляется из стека
Очередь микротасок (Microtask Queue)
После выполнения синхронного кода JavaScript обрабатывает все микротаски перед отрисовкой:
console.log('Синхронный код: 1');
// Микротаски: Promise
Promise.resolve()
.then(() => console.log('Микротаска: Promise'));
// Микротаски: queueMicrotask
queueMicrotask(() => {
console.log('Микротаска: queueMicrotask');
});
console.log('Синхронный код: 2');
// Вывод:
// Синхронный код: 1
// Синхронный код: 2
// Микротаска: Promise
// Микротаска: queueMicrotask
Очередь макротасок (Macrotask Queue)
Макротаски выполняются после всех микротасок и обычно в одной за раз:
console.log('Старт');
setTimeout(() => {
console.log('setTimeout 1 (макротаска)');
// Это обещание выполнится ДО следующей макротаски
Promise.resolve().then(() => {
console.log('Promise после setTimeout');
});
}, 0);
setTimeout(() => {
console.log('setTimeout 2 (макротаска)');
}, 0);
Promise.resolve().then(() => {
console.log('Promise (микротаска)');
});
console.log('Конец');
// Вывод:
// Старт
// Конец
// Promise (микротаска)
// setTimeout 1 (макротаска)
// Promise после setTimeout
// setTimeout 2 (макротаска)
Иерархия выполнения в Event Loop
┌─────────────────────────────────────┐
│ 1. Выполнить синхронный код │
│ (Call Stack пуст) │
└─────────────────────────────────────┘
|
v
┌─────────────────────────────────────┐
│ 2. Обработать ВСЕ микротаски │
│ - Promise │
│ - queueMicrotask │
│ - MutationObserver │
│ (пока очередь не пуста) │
└─────────────────────────────────────┘
|
v
┌─────────────────────────────────────┐
│ 3. Отрисовать (Rendering) │
│ - Обновить DOM │
│ - Пересчитать стили │
│ - Перерисовать страницу │
└─────────────────────────────────────┘
|
v
┌─────────────────────────────────────┐
│ 4. Обработать ОДНУ макротаску │
│ - setTimeout │
│ - setInterval │
│ - setImmediate │
│ - I/O операции │
└─────────────────────────────────────┘
|
v
Вернуться на шаг 2
Практический пример
console.log('1. Начало');
setTimeout(() => {
console.log('2. setTimeout');
Promise.resolve().then(() => {
console.log('3. Promise в setTimeout');
});
}, 0);
Promise.resolve()
.then(() => {
console.log('4. Promise then 1');
return Promise.resolve();
})
.then(() => {
console.log('5. Promise then 2');
});
queueMicrotask(() => {
console.log('6. queueMicrotask');
});
console.log('7. Конец');
// Порядок выполнения:
// 1. Начало (синхронный код)
// 7. Конец (синхронный код)
// 4. Promise then 1 (микротаска)
// 6. queueMicrotask (микротаска)
// 5. Promise then 2 (микротаска - цепочка)
// 2. setTimeout (макротаска)
// 3. Promise в setTimeout (микротаска)
requestAnimationFrame
requestAnimationFrame имеет особую позицию — выполняется ПЕРЕД отрисовкой:
console.log('Синхронный код');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
requestAnimationFrame(() => {
console.log('requestAnimationFrame');
});
// Вывод:
// Синхронный код
// Promise
// requestAnimationFrame
// setTimeout
Практические примеры оптимизации
Проблема: блокирующий синхронный код
// Плохо: заблокирует интерфейс
for (let i = 0; i < 1000000; i++) {
// Тяжелые вычисления
const result = Math.sqrt(i) * Math.sin(i);
}
// Хорошо: разбить на части
function processInChunks(count, chunkSize) {
let i = 0;
function processChunk() {
const end = Math.min(i + chunkSize, count);
while (i < end) {
const result = Math.sqrt(i) * Math.sin(i);
i++;
}
if (i < count) {
// Отпустить главный поток
setTimeout(processChunk, 0);
}
}
processChunk();
}
processInChunks(1000000, 10000);
Оптимизация с микротасками
// Батчинг обновлений состояния
class StateManager {
constructor() {
this.updates = [];
this.scheduled = false;
}
update(fn) {
this.updates.push(fn);
if (!this.scheduled) {
this.scheduled = true;
queueMicrotask(() => this.flush());
}
}
flush() {
const updates = this.updates;
this.updates = [];
this.scheduled = false;
// Применяем все обновления сразу
updates.forEach(fn => fn());
}
}
Рекомендации
- Микротаски быстрее — используйте Promise вместо setTimeout для срочных операций
- requestAnimationFrame идеален для DOM манипуляций и анимаций
- Избегайте длинного синхронного кода — разбивайте на макротаски
- Батчируйте обновления через микротаски для оптимизации
- Помните о порядке — микротаски выполняются перед отрисовкой