← Назад к вопросам
Как работает очередь задач?
2.0 Middle🔥 121 комментариев
#Soft Skills и рабочие процессы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает очередь задач (Event Loop)
Это один из самых важных вопросов для понимания JavaScript. Очередь задач (task queue) — это ключевой механизм асинхронного выполнения кода.
1. Однопоточная природа JavaScript
JavaScript выполняет код в одном потоке, но может обрабатывать асинхронные операции благодаря event loop:
console.log('1. Start');
setTimeout(() => {
console.log('3. setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('2. Promise');
});
console.log('4. End');
// Вывод:
// 1. Start
// 4. End
// 2. Promise
// 3. setTimeout
2. Call Stack, Task Queue, Microtask Queue
Важно понимать три компонента:
// Call Stack - текущий выполняемый код
function outer() {
console.log('outer start');
inner();
console.log('outer end');
}
function inner() {
console.log('inner');
}
outer();
// Call Stack: outer() -> inner() -> back to outer() -> пусто
// Task Queue - макротаски (setTimeout, setInterval, I/O)
setTimeout(() => console.log('task'), 0);
// Microtask Queue - микротаски (Promise, async/await, MutationObserver)
Promise.resolve().then(() => console.log('microtask'));
3. Event Loop - Алгоритм
// Упрощённое представление Event Loop:
// while (eventLoop.waitForTask()) {
// const oldestTask = eventLoop.nextTask();
// execute(oldestTask);
//
// while (microtaskQueue.hasMicrotasks()) {
// const microtask = microtaskQueue.shift();
// execute(microtask);
// }
//
// if (needsRepaint) {
// repaint();
// }
// }
// Это означает:
// 1. Выполнить одну задачу из Task Queue
// 2. Выполнить ВСЕ задачи из Microtask Queue
// 3. Если нужно, перерисовать (requestAnimationFrame)
// 4. Повторить
4. Примеры с разными типами операций
console.log('Start'); // Call Stack, выполняется сразу
setTimeout(() => {
console.log('setTimeout 1');
}, 0); // Task Queue
Promise.resolve()
.then(() => console.log('Promise 1'))
.then(() => console.log('Promise 2')); // Microtask Queue
setImmediate(() => {
console.log('setImmediate');
}); // Task Queue (node.js только)
requestAnimationFrame(() => {
console.log('requestAnimationFrame');
}); // Рисование, не задача
console.log('End'); // Call Stack
// Порядок выполнения:
// 1. Start (Call Stack)
// 2. End (Call Stack)
// 3. Promise 1 (Microtask)
// 4. Promise 2 (Microtask)
// 5. setTimeout 1 (Task)
// 6. setImmediate (Task, node.js)
// 7. requestAnimationFrame (перед следующим frame)
5. Микротаски vs Макротаски
// МИКРОТАСКИ - выполняются в ПРИОРИТЕТЕ:
// - Promise.then/catch/finally
// - async/await
// - queueMicrotask()
// - MutationObserver
// - Process.nextTick (Node.js)
// МАКРОТАСКИ (обычные задачи):
// - setTimeout
// - setInterval
// - setImmediate (Node.js)
// - requestAnimationFrame
// - I/O операции
// - UI события (клик, скролл)
// Почему это важно:
Promise.resolve().then(() => {
console.log('микротаска быстрая');
});
setTimeout(() => {
console.log('макротаска медленнее');
}, 0);
// Микротаска ВСЕГДА выполнится первой, несмотря на setTimeout(0)
6. Практический пример с async/await
async function example() {
console.log('1. Синхронный код в async функции');
await Promise.resolve();
// await переводит остаток функции в микротаску
console.log('2. После await (это микротаска)');
}
example();
console.log('3. После вызова async функции');
setTimeout(() => {
console.log('4. setTimeout');
}, 0);
// Вывод:
// 1. Синхронный код в async функции
// 3. После вызова async функции
// 2. После await (это микротаска)
// 4. setTimeout
7. Проблемы и optimization
// Проблема: Blocking Event Loop
function heavyComputation() {
for (let i = 0; i < 1000000000; i++) {
// Очень долгое вычисление
}
}
heavyComputation(); // Зависает весь UI
button.click(); // Событие не обработается, пока вычисление не закончится
// Решение 1: setTimeout для разбиения на части
function heavyComputationAsync(size = 0) {
if (size < 1000000000) {
setTimeout(() => {
// Небольшое вычисление
heavyComputationAsync(size + 1000);
}, 0);
}
}
// Решение 2: Web Worker
const worker = new Worker('heavy-computation.js');
worker.postMessage(data);
worker.onmessage = (e) => {
console.log('Результат:', e.data);
};
8. Практический пример: правильная обработка асинхронных операций
function fetchUserWithLogging() {
console.log('1. Before fetch');
fetch('/api/user')
.then(response => {
console.log('4. Got response');
return response.json();
})
.then(data => {
console.log('5. Got data:', data);
})
.catch(error => {
console.error('6. Error:', error);
});
console.log('2. After fetch call');
}
Promise.resolve().then(() => {
console.log('3. Microtask');
});
fetchUserWithLogging();
setTimeout(() => {
console.log('7. setTimeout');
}, 0);
// Вывод примерно такой:
// 1. Before fetch
// 2. After fetch call
// 3. Microtask
// 4. Got response (когда fetch завершится)
// 5. Got data
// 7. setTimeout
9. Современный подход: async/await
// Старый способ - Callback Hell
function getUser(id, callback) {
setTimeout(() => callback({ id, name: 'John' }), 100);
}
getUser(1, (user) => {
console.log('User:', user);
});
// Лучше - Promise
function getUserPromise(id) {
return new Promise((resolve) => {
setTimeout(() => resolve({ id, name: 'John' }), 100);
});
}
getUserPromise(1).then(user => console.log('User:', user));
// Лучше всего - async/await
async function getUserAsync(id) {
return new Promise((resolve) => {
setTimeout(() => resolve({ id, name: 'John' }), 100);
});
}
async function main() {
const user = await getUserAsync(1);
console.log('User:', user);
}
main();
Заключение
Очередь задач (Event Loop) - это механизм, позволяющий JavaScript работать асинхронно несмотря на однопоточность. Ключная идея: микротаски (Promises) имеют приоритет над макротасками (setTimeout). Понимание Event Loop критично для написания производительного и предсказуемого кода.