Обработка промисов

Промисы в JavaScript представляют собой объект, который используется для асинхронных операций и позволяет обрабатывать результат операции после её завершения. Промис может находиться в трёх состояниях:

  • pending — начальное состояние, операция ещё не завершена.
  • fulfilled — операция успешно завершена, промис возвращает значение.
  • rejected — операция завершена с ошибкой, промис возвращает причину отказа.

Создание промиса осуществляется с помощью конструктора Promise:

const promise = new Promise((resolve, reject) => {
    // асинхронная операция
    if (успех) {
        resolve("Результат успешной операции");
    } else {
        reject("Ошибка выполнения");
    }
});

resolve и reject — это функции, предоставляемые JavaScript для управления состоянием промиса.


Методы then, catch и finally

После создания промиса можно подключить обработчики с помощью методов then, catch и finally.

  • then(onFulfilled, onRejected) — обрабатывает успешное выполнение промиса. Второй параметр опционален и отвечает за обработку ошибки.
promise.then(
    result => console.log("Успех:", result),
    error => console.log("Ошибка:", error)
);
  • catch(onRejected) — отдельный метод для обработки ошибок, более читаемый способ работы с отклонёнными промисами.
promise
    .then(result => console.log("Успех:", result))
    .catch(error => console.log("Ошибка:", error));
  • finally(callback) — выполняет функцию независимо от результата промиса, часто используется для очистки ресурсов или завершения анимаций.
promise.finally(() => console.log("Операция завершена"));

Цепочки промисов

Одним из ключевых преимуществ промисов является возможность создания цепочек, где результат одного промиса передаётся следующему.

fetch("https://api.example.com/data")
    .then(response => response.json())
    .then(data => {
        console.log("Данные:", data);
        return data.items;
    })
    .then(items => {
        console.log("Элементы:", items);
    })
    .catch(error => {
        console.error("Ошибка при получении данных:", error);
    });

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


Промисы и асинхронные функции

С появлением async/await промисы стали ещё более удобными для работы. Асинхронная функция всегда возвращает промис:

async function fetchData() {
    try {
        const response = await fetch("https://api.example.com/data");
        const data = await response.json();
        console.log("Данные:", data);
    } catch (error) {
        console.error("Ошибка:", error);
    } finally {
        console.log("Запрос завершён");
    }
}
  • await приостанавливает выполнение функции до завершения промиса.
  • Блок try/catch позволяет обрабатывать ошибки асинхронного кода аналогично синхронному.
  • finally выполняется в любом случае, точно так же, как и с обычными промисами.

Статические методы промисов

JavaScript предоставляет несколько полезных статических методов для работы с множеством промисов:

  • Promise.all(iterable) — ждёт завершения всех промисов, возвращает массив результатов или ошибку первого отклонённого промиса.
Promise.all([fetch(url1), fetch(url2)])
    .then(([res1, res2]) => console.log("Два ответа:", res1, res2))
    .catch(error => console.error("Ошибка одного из промисов:", error));
  • Promise.allSettled(iterable) — возвращает массив объектов с состоянием и результатом каждого промиса, независимо от успеха или ошибки.
Promise.allSettled([fetch(url1), fetch(url2)])
    .then(results => console.log("Состояния промисов:", results));
  • Promise.race(iterable) — возвращает результат первого завершившегося промиса, успешного или отклонённого.
Promise.race([fetch(url1), fetch(url2)])
    .then(result => console.log("Первый результат:", result))
    .catch(error => console.error("Первая ошибка:", error));
  • Promise.any(iterable) — возвращает результат первого успешного промиса, отклоняет только если все промисы завершились ошибкой.
Promise.any([fetch(url1), fetch(url2)])
    .then(result => console.log("Первый успешный:", result))
    .catch(error => console.error("Все промисы отклонены:", error));

Отмена промисов и контроль выполнения

Стандартные промисы не имеют встроенной функции отмены. Для управления асинхронными операциями часто используют:

  • AbortController для fetch-запросов.
  • Флаги внутри промисов или кастомные обёртки, которые проверяют состояние выполнения перед обработкой результата.
const controller = new AbortController();
const signal = controller.signal;

fetch("https://api.example.com/data", { signal })
    .then(response => response.json())
    .then(data => console.log("Данные:", data))
    .catch(error => console.error("Ошибка или отмена:", error));

// Отмена запроса
controller.abort();

Практические советы при работе с промисами

  • Всегда обрабатывать ошибки, чтобы избегать “unhandled promise rejection”.
  • Использовать цепочки промисов для последовательной обработки данных.
  • Применять Promise.allSettled для параллельных операций с независимыми результатами.
  • При сложной логике асинхронного кода отдавать предпочтение async/await для читаемости.
  • Не смешивать колбэки и промисы без необходимости, это повышает риск ошибок и ухудшает читаемость.

Эффективное использование промисов позволяет писать асинхронный код, который легко читается, поддерживается и масштабируется.