Перейти к содержимому

async / await

async/await — синтаксический сахар над промисами: компилятор разбивает функцию на microtask-шаги по каждому await, формируя конечный автомат; весь код до первого await выполняется синхронно — это меняет стек и порядок событий.

async function fetchData() {           Трансформация в .then-цепочку:
const a = await step1();    ──→      step1()
const b = await step2(a);   ──→        .then(a => step2(a))
return b + 1;               ──→        .then(b => b + 1)
}                                         .catch(propagate)

Точки разрыва выполнения (microtask границы):

sync         await step1()    await step2(a)    return
│                 │                 │              │
──┼─────────────────┼─────────────────┼──────────────┼──→ время
│            microtask          microtask       microtask
│             (resume)           (resume)       (resolve)
│
└── весь код до первого await выполняется СИНХРОННО
(вызывающий код продолжается после этого)
// async функция ВСЕГДА возвращает Promise
async function getValue() { return 42; }
const p = getValue();
console.log(p instanceof Promise); // true
p.then(v => console.log(v)); // 42
// await работает на любом thenable (duck typing)
const thenable = { then: (resolve) => resolve(99) };
async function test() {
const v = await thenable;
console.log(v); // 99
}
// Синхронная часть до первого await — обычный код
async function sideEffect() {
console.log('sync!'); // выполнится немедленно
await delay(100);
console.log('after delay');
}
sideEffect();
console.log('after sideEffect call'); // sync! → after sideEffect call → after delay
// Параллельный vs последовательный запуск — типичная ошибка
// BAD: последовательно (200ms + 100ms = 300ms)
async function slow() {
const user = await fetchUser(); // 200ms
const posts = await fetchPosts(); // 100ms (ждёт user)
return { user, posts };
}
// GOOD: параллельно (~200ms)
async function fast() {
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
return { user, posts };
}
// Или: запустить промисы заранее, потом await
async function alsoFast() {
const userP = fetchUser(); // стартует немедленно
const postsP = fetchPosts(); // стартует немедленно
return { user: await userP, posts: await postsP };
}
// await в цикле: sequential vs parallel vs batched
// Sequential (порядок важен / rate-limit):
for (const id of ids) {
await processItem(id); // каждый ждёт предыдущего
}
// Parallel (все сразу):
await Promise.all(ids.map(id => processItem(id)));
// Batched parallel (по N штук):
async function batchProcess(items, batchSize = 5) {
for (let i = 0; i < items.length; i += batchSize) {
await Promise.all(
items.slice(i, i + batchSize).map(processItem)
);
}
}

Итог: async/await — синтаксический сахар над .then()-цепочкой; компилятор создаёт конечный автомат, каждый await — точка разрыва с возобновлением через microtask queue.