Как может асинхронный код выполняться параллельно с синхронным?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как может асинхронный код выполняться параллельно с синхронным?
Это возможно благодаря Event Loop в JavaScript. Язык работает в одном потоке, но с помощью асинхронного выполнения может создавать иллюзию параллелизма, когда асинхронный код и синхронный выполняются "одновременно".
Event Loop: основы
JavaScript работает в одном потоке (single-threaded), но благодаря Event Loop асинхронный код может выполняться "параллельно" с синхронным. На самом деле это называется сосуществование — не параллелизм, а переключение контекста.
console.log('1. Начало');
setTimeout(() => {
console.log('2. Асинхронный код из setTimeout');
}, 0);
console.log('3. Конец синхронного кода');
// Вывод:
// 1. Начало
// 3. Конец синхронного кода
// 2. Асинхронный код из setTimeout
Как это работает: архитектура Event Loop
JavaScript имеет три ключевых компонента:
- Call Stack — стек вызовов (выполняет синхронный код)
- Web API — браузерные API (XMLHttpRequest, setTimeout, fetch)
- Event Queue — очередь событий (асинхронные операции)
┌─────────────────────────────────────────┐
│ JavaScript Engine │
│ ┌──────────────┐ │
│ │ Call Stack │ (синхронный код) │
│ └──────────────┘ │
└─────────────────────────────────────────┘
↕
┌──────────────────┐
│ Web APIs │
│ setTimeout │
│ fetch │
│ XMLHttpRequest │
└──────────────────┘
↕
┌──────────────────┐
│ Event Queue │
│ (Callback Queue)│
└──────────────────┘
Практический пример
console.log('1. Синхронный код 1');
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log('3. Асинхронный код (fetch завершен)');
});
setTimeout(() => {
console.log('4. Асинхронный код (setTimeout)');
}, 1000);
console.log('2. Синхронный код 2');
// Порядок выполнения:
// 1. Синхронный код 1
// 2. Синхронный код 2
// 3. Асинхронный код (fetch завершен) — когда fetch завершится
// 4. Асинхронный код (setTimeout) — через 1 секунду
Почему асинхронный код не блокирует синхронный?
Асинхронные операции делегируются браузеру/операционной системе:
// Синхронная операция (блокирует)
function blockingLoop() {
for (let i = 0; i < 1000000000; i++) {
// Долгая операция — пока она выполняется,
// ничего другого не может выполниться
}
}
blockingLoop(); // UI зависнет!
// Асинхронная операция (не блокирует)
setTimeout(() => {
for (let i = 0; i < 1000000000; i++) {
// Эта операция делегирована асинхронному выполнению
// UI остаётся отзывчивым
}
}, 0);
Promise и Event Loop
Promise используют Microtask Queue, которая имеет больший приоритет, чем Event Queue:
console.log('1. Синхронный код');
setTimeout(() => {
console.log('4. setTimeout (Macrotask)');
}, 0);
Promise.resolve()
.then(() => {
console.log('2. Promise (Microtask)');
});
console.log('3. Ещё синхронный код');
// Вывод:
// 1. Синхронный код
// 3. Ещё синхронный код
// 2. Promise (Microtask) — выполнится раньше setTimeout!
// 4. setTimeout (Macrotask)
Очерёдность выполнения (Priority)
// Высший приоритет → Низший приоритет
// 1. Call Stack (синхронный код)
// 2. Microtask Queue (Promises, async/await)
// 3. Macrotask Queue (setTimeout, setInterval, events)
console.log('1. Синхронно');
setTimeout(() => console.log('4. Macrotask (setTimeout)'), 0);
Promise.resolve()
.then(() => console.log('2. Microtask (Promise)'));
Promise.resolve()
.then(() => console.log('3. Microtask (Promise 2)'));
// Результат:
// 1. Синхронно
// 2. Microtask (Promise)
// 3. Microtask (Promise 2)
// 4. Macrotask (setTimeout)
Реальный пример: загрузка данных
function loadUserData(userId) {
console.log('1. Начало запроса'); // Синхронно
fetch(`/api/user/${userId}`)
.then(response => response.json()) // Microtask
.then(data => {
console.log('3. Данные получены:', data); // Microtask
updateUI(data);
})
.catch(error => {
console.error('Ошибка:', error); // Microtask
});
console.log('2. Запрос инициирован'); // Синхронно
}
loadUserData(123);
// Вывод:
// 1. Начало запроса
// 2. Запрос инициирован
// 3. Данные получены: {...}
async/await и Event Loop
async function processData() {
console.log('1. Синхронно в начале функции');
const data = await fetch('/api/data').then(r => r.json());
// Здесь выполнение функции приостанавливается
console.log('3. Асинхронно после await');
}
console.log('2. Синхронно вне функции');
processData();
// Вывод:
// 1. Синхронно в начале функции
// 2. Синхронно вне функции
// 3. Асинхронно после await
Best Practices
✅ Понимание Event Loop:
// Используй это понимание для оптимизации
// Microtask (Promises) выполнятся ДО Macrotask (setTimeout)
async function efficient() {
const data = await fetch('/api/data').then(r => r.json());
// Выполнится быстрее, чем setTimeout
}
✅ Избегай блокирования синхронным кодом:
// Плохо: блокирует UI
function slowSync() {
const result = heavyComputation(); // Блокирует!
return result;
}
// Хорошо: не блокирует
async function fastAsync() {
const result = await heavyComputationAsync(); // Не блокирует
return result;
}
Вывод: асинхронный код может выполняться "параллельно" с синхронным благодаря Event Loop. JavaScript обрабатывает синхронный код в Call Stack, а асинхронные операции делегирует браузеру, который возвращает результаты в очередь событий. Event Loop постоянно проверяет эту очередь и выполняет следующий callback только когда Call Stack пуст.