Что такое Promise в рамках Event Loop?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое Promise в рамках Event Loop?
Promise — это фундаментальный концепт для понимания асинхронного кода в JavaScript. Чтобы полностью разобраться, нужно рассмотреть его взаимодействие с Event Loop.
Что такое Promise
Promise — это объект, представляющий будущий результат асинхронной операции. У него есть три состояния:
- pending — операция ещё выполняется
- fulfilled — операция успешно завершена, получен результат
- rejected — операция завершилась ошибкой
const promise = new Promise((resolve, reject) => {
// Асинхронная операция
setTimeout(() => {
resolve('Успех!');
}, 1000);
});
promise
.then(result => console.log(result)) // Успех!
.catch(error => console.error(error));
Event Loop: основы
Event Loop — это механизм, который постоянно проверяет, есть ли работа, и выполняет её в правильном порядке.
структура Event Loop:
┌─────────────────────────┐
│ Call Stack │ <- Текущий код
├─────────────────────────┤
│ Microtask Queue │ <- Promise callbacks
│ (Microtasks) │
├─────────────────────────┤
│ Macrotask Queue │ <- setTimeout, setInterval
│ (Macrotasks) │
└─────────────────────────┘
Promise и Microtask Queue
Это критически важное различие:
Promises используют Microtask Queue:
- Callbacks от
.then(),.catch(),.finally()— это микротаски - Микротаски выполняются сразу после выполнения текущего кода
- Но ДО выполнения следующего макротаска (setTimeout)
setTimeout использует Macrotask Queue:
- Callback от
setTimeout— это макротаска - Выполняется после всех микротасков
Порядок выполнения (очень важно!)
console.log('1. Синхронный код');
setTimeout(() => {
console.log('4. setTimeout (макротаска)');
}, 0);
Promise.resolve()
.then(() => {
console.log('2. Promise.then() (микротаска)');
})
.then(() => {
console.log('3. Promise.then() (микротаска)');
});
console.log('1.5 Синхронный код');
// Вывод:
// 1. Синхронный код
// 1.5 Синхронный код
// 2. Promise.then() (микротаска)
// 3. Promise.then() (микротаска)
// 4. setTimeout (макротаска)
Почему так происходит?
- Event Loop выполняет весь синхронный код в Call Stack (1, 1.5)
- Синхронный код закончился, Stack пуст
- Event Loop проверяет Microtask Queue — выполняет все Promise callbacks (2, 3)
- Microtask Queue пуста
- Event Loop проверяет Macrotask Queue — выполняет setTimeout (4)
Практический пример с fetch
console.log('Начало');
fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log('Данные получены:', data);
})
.catch(error => {
console.error('Ошибка:', error);
});
setTimeout(() => {
console.log('Таймер закончился');
}, 100);
console.log('Конец');
// Вывод:
// Начало
// Конец
// Данные получены: {...} <- выполнится раньше, чем таймер!
// Таймер закончился
Почему Promise выполнится раньше setTimeout? Потому что Promise использует Microtask Queue, которая имеет больший приоритет!
Async/Await в контексте Event Loop
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data; // Это Promise!
} catch (error) {
console.error(error);
}
}
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
await — это синтаксический сахар над .then()
awaitпаузирует выполнение async функции- Когда Promise разрешится, выполнение продолжается
- Все это происходит в Microtask Queue!
Трудные случаи
Пример 1: Множественные Promise в цепочке
Promise.resolve(1)
.then(x => Promise.resolve(x + 1))
.then(x => Promise.resolve(x + 1))
.then(x => console.log(x)); // 3
// Каждый .then() добавляет новую микротаску
// Все они выполняются в одном Event Loop tick'е
Пример 2: Смешивание микро- и макротасок
for (let i = 0; i < 2; i++) {
setTimeout(() => console.log('macro', i), 0);
Promise.resolve()
.then(() => console.log('micro', i));
}
// Вывод:
// micro 0
// micro 1
// macro 0
// macro 1
Практическое применение во Frontend
1. Оптимизация производительности
// Плохо: блокирует UI
for (let i = 0; i < 1000; i++) {
doHeavyWork(i);
}
// Хорошо: разбиваем на макротаски
function processInChunks(items, callback) {
let index = 0;
function process() {
if (index < items.length) {
callback(items[index]);
index++;
setTimeout(process, 0); // Следующий макротаск
}
}
process();
}
2. Тестирование асинхронного кода
// Jest/Vitest
test('Promise должна разрешиться', async () => {
const result = await fetchData();
expect(result).toBe('expected');
});
// Важно: await дождётся микротаски!
Заключение
Ключевые моменты:
- Promise = Microtask Queue — выполняется раньше setTimeout
- Event Loop приоритизирует: Синхронный код → Микротаски → Макротаски
- Все .then() callbacks — это микротаски, выполняются в едином "батче"
- Понимание этого критично для оптимизации и отладки асинхронного кода