# Промисы и async/await (JS)

> Механизм асинхронного программирования в JavaScript — объект Promise как обещание будущего результата и синтаксис async/await поверх него.

Источник: https://llmdocs.ru/wiki/js-promises-async/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Язык**: JavaScript (ECMAScript)
- **Promise в стандарте**: ES2015 (ES6)
- **async/await в стандарте**: ES2017
- **Предшественники**: колбэки, Promises/A+, jQuery Deferred
- **Состояния промиса**: pending, fulfilled, rejected
- **Комбинаторы**: Promise.all, allSettled, race, any
**Промис** (англ. *Promise* — обещание) — объект [JavaScript](https://llmdocs.ru/wiki/javascript.md), представляющий результат асинхронной операции, который будет известен позже: ответ сервера, содержимое файла, срабатывание таймера. **async/await** — синтаксис, позволяющий писать код с промисами так, будто он выполняется последовательно. Вместе они — основной способ работы с асинхронностью в браузере и в [Node.js](https://llmdocs.ru/wiki/nodejs.md).[2]

## Зачем это нужно

JavaScript выполняется в одном потоке с циклом событий: долгие операции нельзя ждать синхронно, иначе страница или сервер «замёрзнут». Исторически результат передавали в **колбэк** — функцию, вызываемую по готовности. При нескольких зависимых шагах колбэки вкладывались друг в друга («ад колбэков», *callback hell*), а обработку ошибок приходилось повторять на каждом уровне.

Идея «обещаний» (futures) известна с 1970-х; в JavaScript её популяризировали библиотеки (Q, Bluebird, jQuery Deferred), а совместимость между ними обеспечила спецификация Promises/A+ (2012).[4] Встроенный `Promise` вошёл в стандарт ES2015, синтаксис `async`/`await` — в ES2017.[1]

## Промис

У промиса три состояния: **ожидание** (pending), **выполнен** (fulfilled) со значением, **отклонён** (rejected) с причиной. Из ожидания он переходит ровно один раз и дальше не меняется.

```js
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

fetch("/api/articles")
  .then((res) => {
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return res.json();             // возврат промиса — цепочка ждёт его
  })
  .then((data) => console.log(data.length))
  .catch((err) => console.error("ошибка:", err))   // ловит ошибки всей цепочки
  .finally(() => console.log("готово"));
```

Метод `then` всегда возвращает новый промис, поэтому вызовы выстраиваются в плоскую цепочку, а один `catch` в конце обрабатывает ошибку на любом шаге — аналог `try/catch` для асинхронного кода.

## async/await

Функция, объявленная с `async`, всегда возвращает промис. Внутри неё `await` приостанавливает выполнение функции (но не потока!) до разрешения промиса и возвращает его значение или выбрасывает исключение.[3]

```js
async function loadArticle(slug) {
  try {
    const res = await fetch(`/wiki/${slug}.md`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.text();
  } catch (err) {
    console.error(err);
    return null;
  }
}
```

Код выглядит синхронным, но пока функция ждёт ответа, цикл событий обрабатывает другие задачи. В ES-модулях разрешён `await` на верхнем уровне. Сходная модель есть в Python — [asyncio](https://llmdocs.ru/wiki/asyncio.md) — и в Rust ([async в Rust и Tokio](https://llmdocs.ru/wiki/rust-async-tokio.md)).

## Параллельное ожидание

Частая ошибка — последовательные `await` для независимых операций: каждый запрос ждёт предыдущий. Комбинаторы запускают их одновременно:

| Метод | Результат |
|---|---|
| `Promise.all(list)` | массив значений; отклоняется при первой ошибке |
| `Promise.allSettled(list)` | статус каждого промиса, никогда не отклоняется |
| `Promise.race(list)` | первый завершившийся (успехом или ошибкой) |
| `Promise.any(list)` | первый успешный; ошибка, только если упали все |

```js
const [user, posts] = await Promise.all([getUser(id), getPosts(id)]);

// таймаут для запроса
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
```

Отменить сам промис нельзя; для отмены операций используется `AbortController` и его сигнал, который понимают `fetch` и многие API Node.js. Метод `Promise.withResolvers()` (ES2024) упрощает создание промиса, разрешаемого извне.[6]

## Микрозадачи

Колбэки промисов выполняются как **микрозадачи**: после текущего синхронного кода, но раньше обычных задач (таймеров, событий ввода-вывода).[5]

```js
setTimeout(() => console.log("3: задача"), 0);
Promise.resolve().then(() => console.log("2: микрозадача"));
console.log("1: синхронно");
```

Поэтому бесконечная цепочка микрозадач способна заблокировать отрисовку страницы так же, как синхронный цикл.

## Типичные ошибки

- Забытый `await` — функция получает промис вместо значения; [TypeScript](https://llmdocs.ru/wiki/typescript.md) и линтеры помогают это поймать.
- Необработанное отклонение — в Node.js по умолчанию завершает процесс.
- `await` внутри `forEach` не ждёт — нужен `for...of` или `Promise.all` с `map`.

## См. также

- [JavaScript](https://llmdocs.ru/wiki/javascript.md)
- [Node.js](https://llmdocs.ru/wiki/nodejs.md)
- [asyncio](https://llmdocs.ru/wiki/asyncio.md)
- [Конкурентность и параллелизм](https://llmdocs.ru/wiki/concurrency-parallelism.md)
- [DOM](https://llmdocs.ru/wiki/dom.md)

## Источники

1. [ECMAScript Language Specification — Promise Objects](https://tc39.es/ecma262/#sec-promise-objects)
2. [MDN — Using promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)
3. [MDN — async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
4. [Promises/A+ specification](https://promisesaplus.com/)
5. [HTML Standard — Event loops (tasks and microtasks)](https://html.spec.whatwg.org/multipage/webappapis.html#event-loops)
6. [MDN — Promise.withResolvers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers)
