Урок 10

Асинхронность: промисы

Промис — объект, представляющий результат асинхронной операции. Может быть в трёх состояниях: pending, fulfilled, rejected.

Создание

const promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve('готово'), 1000);
});

then / catch / finally

promise
    .then((value) => console.log(value))
    .catch((error) => console.error(error))
    .finally(() => console.log('завершено'));

Цепочка

fetch('/api/user')
    .then((res) => res.json())
    .then((user) => console.log(user))
    .catch((err) => console.error(err));

Состояния

  • pending — ожидание
  • fulfilled — выполнено с результатом
  • rejected — отклонено с ошибкой

После перехода в fulfilled или rejected промис не меняется.

Комбинаторы

Promise.all

Ждёт все, возвращает массив. Если один упадёт — весь промис упадёт.

const [a, b, c] = await Promise.all([
    fetch('/a').then((r) => r.json()),
    fetch('/b').then((r) => r.json()),
    fetch('/c').then((r) => r.json()),
]);

Promise.allSettled

Ждёт все, возвращает результаты с status:

const results = await Promise.allSettled([p1, p2]);
// [{ status: 'fulfilled', value }, { status: 'rejected', reason }]

Promise.race

Первый завершённый (успех или ошибка):

const result = await Promise.race([fetch('/fast'), fetch('/slow')]);

Promise.any

Первый успешный:

const result = await Promise.any([p1, p2]);

Если все упадут — AggregateError.

Асинхронная функция

async function loadUser(id) {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
}

async всегда возвращает промис.

await

const user = await loadUser(1);

Приостанавливает выполнение функции до разрешения промиса.

Обработка ошибок

async function load() {
    try {
        const data = await fetchData();
        return data;
    } catch (error) {
        console.error(error);
        throw error;
    } finally {
        console.log('готово');
    }
}

Последовательное vs параллельное

Последовательно (медленнее):

const a = await fetchA();
const b = await fetchB();

Параллельно:

const [a, b] = await Promise.all([fetchA(), fetchB()]);

Создание промиса

function delay(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

await delay(1000);

Промисификация

function readFile(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, (err, data) => {
            if (err) reject(err);
            else resolve(data);
        });
    });
}

Утечки

Если промис отклонён и не обработан — UnhandledPromiseRejection. Всегда добавляй catch или try/catch.

Итоги

  • Промис — результат асинхронной операции
  • then, catch, finally
  • async/await — синтаксический сахар
  • Promise.all для параллельности
  • allSettled когда нужны все результаты
  • Обрабатывай ошибки