Урок 03

Промисы: основы

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

Состояния

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

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

Создание

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('готово');
        // или reject(new Error('ошибка'));
    }, 1000);
});

resolve — успех, reject — ошибка. Вызывается только один раз.

then, catch, finally

promise
    .then((value) => console.log(value))
    .catch((error) => console.error(error))
    .finally(() => console.log('завершено'));
  • then — на успех
  • catch — на ошибку
  • finally — в любом случае

Цепочка

then возвращает новый промис. Можно строить цепочки:

fetch('/api/user')
    .then((res) => res.json())
    .then((user) => fetch(`/api/posts?userId=${user.id}`))
    .then((res) => res.json())
    .then((posts) => console.log(posts))
    .catch((err) => console.error(err));

Каждый then получает результат предыдущего.

Возврат значения

Если then возвращает значение — оно попадёт в следующий then:

Promise.resolve(1)
    .then((x) => x + 1)
    .then((x) => x * 2)
    .then((x) => console.log(x)); // 4

Если возвращает промис — цепочка ждёт его:

fetchUser()
    .then((user) => fetchPosts(user.id))
    .then((posts) => console.log(posts));

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

Один catch ловит ошибки из всей цепочки:

fetchData()
    .then((data) => process(data))
    .then((result) => save(result))
    .catch((err) => console.error('Где-то упало:', err));

Можно бросать ошибки вручную:

.then((res) => {
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return res.json();
})
.catch((err) => console.error(err.message));

finally

Выполняется всегда:

showSpinner();

fetchData()
    .then((data) => display(data))
    .catch((err) => showError(err))
    .finally(() => hideSpinner());

Promise.resolve и Promise.reject

Создание уже разрешённого промиса:

Promise.resolve(42).then((v) => console.log(v));
Promise.reject(new Error('fail')).catch((e) => console.error(e));

Промисы всегда асинхронны

Даже Promise.resolve выполняется после синхронного кода:

console.log('1');

Promise.resolve().then(() => console.log('2'));

console.log('3');

// 1, 3, 2

Вложенные промисы

Не пихай промис внутрь промиса:

// Плохо
fetchUser().then((user) => {
    fetchPosts(user.id).then((posts) => {
        console.log(posts);
    });
});

// Хорошо
fetchUser()
    .then((user) => fetchPosts(user.id))
    .then((posts) => console.log(posts));

Вложенность теряет catch.

Итоги

  • Промис — результат асинхронной операции
  • Три состояния: pending, fulfilled, rejected
  • then, catch, finally
  • Цепочки через then
  • Одна точка обработки ошибок на всю цепочку
  • Промисы всегда асинхронны