В чем разница между аsync/await и рromise


async/await и Promise — это два взаимосвязанных механизма для работы с асинхронным кодом в JavaScript, но с разными уровнями абстракции и синтаксисом. Оба опираются на одно и то же основание — Promise, но async/await предоставляет более удобный, синхроноподобный способ записи асинхронного кода.

🔹 Что такое Promise

Promise — это объект, представляющий будущее завершение (или неудачу) асинхронной операции и её результат. Он может находиться в одном из трёх состояний:

  1. Pending (ожидание) — операция ещё не завершена;

  2. Fulfilled (успешно выполнено) — операция завершена успешно;

  3. Rejected (отклонено) — операция завершена с ошибкой.

Создание и использование Promise:

const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Результат");
// reject("Ошибка");
}, 1000);
});
promise
.then(result => console.log(result))
.catch(error => console.error(error));

Здесь then обрабатывает успешное завершение, catch — ошибку. Обработчики можно вызывать цепочкой: .then().then().catch()

🔹 Что такое async/await

async/await — это синтаксический сахар над Promise, который позволяет писать асинхронный код, выглядящий как синхронный. Это упрощает чтение и сопровождение кода, особенно при последовательных или вложенных асинхронных вызовах.

  • Ключевое слово async используется перед функцией — оно автоматически возвращает Promise.

  • Ключевое слово await используется для приостановки выполнения до завершения Promise.

Пример с async/await:

async function fetchData() {
try {
const result = await someAsyncOperation();
console.log(result);
} catch (error) {
console.error(error);
}
}

Это аналогично:

someAsyncOperation()
.then(result => console.log(result))
.catch(error => console.error(error));

🔸 Отличия между Promise и async/await

Категория Promise async/await
Основан на Собственная реализация Promise под капотом
--- --- ---
Синтаксис Цепочки .then() и .catch() Более линейный и читаемый синтаксис
--- --- ---
Обработка ошибок Метод .catch() Конструкция try...catch
--- --- ---
Уровень абстракции Ближе к "ручному" управлению Более высокоуровневый
--- --- ---
Удобство при множественных вызовах Может быть сложно при глубокой вложенности Проще писать цепочки, похожие на синхронный код
--- --- ---
Совместимость Поддерживается с ES6 Появился в ES2017 (ES8)
--- --- ---
Возвращаемое значение Возвращает объект Promise Также возвращает Promise
--- --- ---
Параллелизм Можно реализовать с Promise.all() Также можно с await Promise.all([...])
--- --- ---
Отладка Сложнее из-за цепочек Проще: стек вызовов ближе к обычным функциям
--- --- ---

🔸 Пример сравнения

С использованием Promise:

function getUserData() {
return fetch("/user")
.then(res => res.json())
.then(user => {
return fetch(\`/user/${user.id}/posts\`)
.then(res => res.json());
})
.catch(error => console.error(error));
}

С использованием async/await:

async function getUserData() {
try {
const resUser = await fetch("/user");
const user = await resUser.json();
const resPosts = await fetch(\`/user/${user.id}/posts\`);
const posts = await resPosts.json();
return posts;
} catch (error) {
console.error(error);
}
}

🔸 Обработка нескольких асинхронных операций

  • Параллельно:
// Promise:
Promise.all(\[fetch(url1), fetch(url2)\]).then((\[res1, res2\]) => {});
// async/await:
const \[res1, res2\] = await Promise.all(\[fetch(url1), fetch(url2)\]);
  • Последовательно:
// async/await
const data1 = await fetch(url1);
const data2 = await fetch(url2); // ждет завершения первого

🔸 Ошибки и подводные камни

1. await работает только внутри async функции:

await somePromise(); //  SyntaxError

2. async функция всегда возвращает Promise:

async function foo() {
return 42;
}
foo().then(console.log); // 42

3. Обработка ошибок в async/await требует try...catch:

try {
await failingPromise();
} catch (err) {
console.error("Ошибка:", err);
}

4. Последовательное выполнение может замедлить выполнение:

//  так делать неэффективно, если вызовы независимы
const a = await fetchA();
const b = await fetchB();

Лучше:

//  параллельно
const \[a, b\] = await Promise.all(\[fetchA(), fetchB()\]);

🔸 В каких случаях что использовать

  • Promise:

    • Подходит для базовых асинхронных операций;

    • Удобен при необходимости построения сложной цепочки, особенно с промежуточной логикой;

    • Используется в библиотеках, где необходимо программно комбинировать и управлять обещаниями.

  • async/await:

    • Предпочтительнее в прикладной логике и бизнес-коде;

    • Читаемее и удобнее для линейного кода;

    • Легче отлаживать и сопровождать.

Таким образом, async/await — это не альтернатива Promise, а более удобный способ их использования. Все асинхронные функции с async возвращают Promise, а await просто приостанавливает выполнение до его разрешения.