Что возвращают методы .then(), .catch(), .finally()?

👨‍💻 Frontend Developer 🟡 Часто попадается 🎚️ Средний
#JavaScript #Асинхронность #База JS

Краткий ответ

Методы Promise .then(), .catch() и .finally() всегда возвращают новый Promise, что позволяет создавать цепочки асинхронных операций.

  • .then() возвращает Promise, который разрешается со значением, возвращаемым обработчиком
  • .catch() возвращает Promise, который разрешается со значением, возвращаемым обработчиком ошибок
  • .finally() возвращает Promise, который разрешается с исходным значением Promise, независимо от результата обработчика

Полный ответ

Понимание того, что возвращают методы .then(), .catch() и .finally(), критически важно для эффективной работы с цепочками Promise в JavaScript. Все три метода возвращают новые Promise, что делает возможным создание сложных асинхронных последовательностей.

Что возвращает .then()

Метод .then() принимает два необязательных параметра (обработчика) и всегда возвращает новый Promise:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve('начальное значение'), 1000);
});
 
const newPromise = promise.then(
  value => {
    console.log(value); // 'начальное значение'
    return 'новое значение';
  },
  error => {
    console.error(error);
    return 'значение при ошибке';
  }
);
 
// newPromise - это новый Promise
newPromise.then(result => {
  console.log(result); // 'новое значение'
});

Варианты возвращаемых значений .then()

  1. Возврат обычного значения:
promise.then(value => {
  return 'новая строка';
}).then(result => {
  console.log(result); // 'новая строка'
});
  1. Возврат другого Promise:
promise.then(value => {
  return fetch('/api/data'); // Возвращает Promise
}).then(response => {
  return response.json(); // Работаем с результатом fetch
}).then(data => {
  console.log(data); // Данные из API
});
  1. Без возврата значения (undefined):
promise.then(value => {
  console.log(value);
  // Неявно возвращает undefined
}).then(result => {
  console.log(result); // undefined
});
  1. Выброс исключения:
promise.then(value => {
  throw new Error('Ошибка в обработчике');
}).catch(error => {
  console.log(error.message); // 'Ошибка в обработчике'
});

Что возвращает .catch()

Метод .catch() также возвращает новый Promise и используется для обработки ошибок:

const errorPromise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('Ошибка')), 1000);
});
 
const newPromise = errorPromise.catch(error => {
  console.log(error.message); // 'Ошибка'
  return 'восстановленное значение';
});
 
newPromise.then(result => {
  console.log(result); // 'восстановленное значение'
});

Особенности возврата .catch()

  1. Успешная обработка ошибки:
fetch('/api/data')
  .then(response => response.json())
  .catch(error => {
    console.error('Ошибка:', error);
    return { error: true, data: [] }; // Восстановление после ошибки
  })
  .then(result => {
    console.log(result); // { error: true, data: [] }
  });
  1. Повторный выброс ошибки:
promise.catch(error => {
  console.error('Первичная обработка:', error);
  throw error; // Повторный выброс
}).catch(error => {
  console.error('Вторичная обработка:', error);
});

Что возвращает .finally()

Метод .finally() возвращает новый Promise, но с важной особенностью - он передает исходное значение Promise, независимо от возвращаемого значения обработчика:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve('успешное значение'), 1000);
});
 
const newPromise = promise.finally(() => {
  console.log('Очистка ресурсов');
  return 'игнорируемое значение';
});
 
newPromise.then(result => {
  console.log(result); // 'успешное значение' (не 'игнорируемое значение')
});

Особенности возврата .finally()

  1. Передача исходного значения при успехе:
Promise.resolve('данные')
  .finally(() => {
    console.log('Завершение');
    return 'игнорируется';
  })
  .then(result => {
    console.log(result); // 'данные'
  });
  1. Передача исходной ошибки при неудаче:
Promise.reject(new Error('ошибка'))
  .finally(() => {
    console.log('Завершение');
    return 'игнорируется';
  })
  .catch(error => {
    console.log(error.message); // 'ошибка'
  });

Цепочки возвратов

Понимание возвращаемых значений позволяет создавать сложные цепочки:

fetch('/api/user')
  .then(response => {
    if (!response.ok) throw new Error('Ошибка сети');
    return response.json();
  })
  .then(user => {
    console.log('Пользователь загружен:', user);
    return fetch(`/api/profile/${user.id}`);
  })
  .then(response => response.json())
  .then(profile => {
    console.log('Профиль загружен:', profile);
    return profile;
  })
  .catch(error => {
    console.error('Ошибка в цепочке:', error);
    return { error: true };
  })
  .finally(() => {
    console.log('Запрос завершен');
  });

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

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

function loadUserData(userId) {
  return fetch(`/api/users/${userId}`)
    .then(response => {
      if (!response.ok) throw new Error('Пользователь не найден');
      return response.json();
    })
    .then(user => {
      // Загружаем дополнительные данные
      return Promise.all([
        Promise.resolve(user),
        fetch(`/api/posts/${userId}`).then(res => res.json()),
        fetch(`/api/comments/${userId}`).then(res => res.json())
      ]);
    })
    .then(([user, posts, comments]) => {
      return { user, posts, comments };
    })
    .catch(error => {
      console.error('Ошибка загрузки:', error);
      return null;
    });
}

Преобразование данных в цепочке

fetch('/api/products')
  .then(response => response.json())
  .then(products => {
    // Фильтрация данных
    return products.filter(product => product.price > 0);
  })
  .then(filteredProducts => {
    // Сортировка данных
    return filteredProducts.sort((a, b) => b.price - a.price);
  })
  .then(sortedProducts => {
    // Преобразование данных
    return sortedProducts.map(product => ({
      ...product,
      displayName: `${product.name} ($${product.price})`
    }));
  })
  .then(finalProducts => {
    console.log('Обработанные продукты:', finalProducts);
    return finalProducts;
  });

Распространенные ошибки

1. Забытый return в цепочке

// ❌ Неправильно - потеря значения в цепочке
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    processData(data); // Забыли return
  })
  .then(result => {
    console.log(result); // undefined
  });
 
// ✅ Правильно - возврат значения
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    return processData(data); // Возвращаем результат
  })
  .then(result => {
    console.log(result);
  });

2. Неправильная обработка ошибок в цепочке

// ❌ Ошибка может быть утеряна
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    throw new Error('Ошибка обработки');
  })
  .then(result => {
    console.log(result);
  });
 
// ✅ Правильная обработка
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    throw new Error('Ошибка обработки');
  })
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Ошибка:', error);
  });

Лучшие практики

  1. Всегда возвращайте значения в обработчиках .then() для поддержания цепочки
  2. Используйте .catch() для централизованной обработки ошибок
  3. Используйте .finally() для очистки ресурсов независимо от результата
  4. Избегайте вложенности - создавайте цепочки вместо вложенных .then()
  5. Понимайте разницу между возвращаемыми значениями каждого метода

Ключевые особенности

  1. Все методы возвращают Promise - это основа для цепочек
  2. .then() и .catch() передают возвращаемые значения следующим обработчикам
  3. .finally() игнорирует возвращаемое значение и передает исходное
  4. Исключения автоматически передаются следующему .catch() в цепочке
  5. Цепочки выполняются последовательно - каждый шаг ждет завершения предыдущего

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


Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪