then/catch/finally: цепочки промисов

Как работают цепочки промисов? 🔗

Представьте, что вы шеф-повар, который готовит сложное блюдо по шагам. Каждый шаг зависит от предыдущего — нельзя нарезать овощи, пока их не купили, и нельзя подавать блюдо, пока оно не приготовлено. Вот так же работают цепочки промисов в JavaScript!

// Простая цепочка промисов
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => processData(data))
  .catch(error => console.error('Ошибка:', error))
  .finally(() => console.log('Запрос завершён'))

then() — ваш верный помощник ⛓️

Метод then() — это фундамент цепочек. Он принимает два аргумента (хотя второй необязателен):

  1. Функция, которая выполнится при успешном выполнении промиса
  2. Функция для обработки ошибок (но лучше использовать catch)
// Практический пример с then()
function getUser(id) {
  return fetch(`/api/users/${id}`)
    .then(response => {
      if (!response.ok) throw new Error('Ошибка сети')
      return response.json()
    })
    .then(user => {
      console.log('Пользователь получен:', user)
      return user
    })
}

💡 Важно: каждый then() возвращает новый промис, поэтому цепочки можно продолжать бесконечно!


catch() — спасательный круг 🛟

Когда в цепочке происходит ошибка, JavaScript ищет ближайший catch(). Это как аварийный выход — можно либо обработать ошибку, либо продолжить цепочку с новым значением.

// Обработка ошибок с style
fetch('/api/orders/123')
  .then(response => response.json())
  .then(order => {
    if (!order.valid) throw new Error('Недействительный заказ')
    return processOrder(order)
  })
  .catch(error => {
    console.error('Ошибка заказа:', error.message)
    return getDefaultOrder() // Продолжаем цепочку с запасным вариантом
  })
  .then(finalOrder => {
    // Сюда попадём в любом случае — с обработанной ошибкой или без
    displayOrder(finalOrder)
  })

finally() — финальный аккорд 🎹

finally() выполнится в любом случае — был ли промис успешным или завершился с ошибкой. Идеально для очистки ресурсов или скрытия лоадеров.

let isLoading = true

fetch('/api/products')
  .then(response => response.json())
  .then(products => renderProducts(products))
  .catch(error => showError(error))
  .finally(() => {
    isLoading = false
    console.log('Загрузка завершена')
  })

🔥 Интересный факт: finally() не получает аргументов и не влияет на возвращаемое значение цепочки.


Комбинируем всё вместе 🧩

Давайте соберём полноценный пример, имитирующий реальный сценарий:

// Загрузка пользовательских данных с обработкой всех случаев
function loadUserProfile(userId) {
  let loading = true
  let profile = null

  fetch(`/api/users/${userId}`)
    .then(response => {
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
      return response.json()
    })
    .then(userData => {
      profile = transformUserData(userData)
      return fetch(`/api/users/${userId}/friends`)
    })
    .then(friendsResponse => friendsResponse.json())
    .then(friends => {
      profile.friends = friends
      return profile
    })
    .catch(error => {
      console.error('Ошибка загрузки:', error)
      return getCachedProfile(userId) // Пробуем запасной вариант
    })
    .finally(() => {
      loading = false
      document.querySelector('#loader').style.display = 'none'
    })
    .then(finalProfile => {
      renderUserProfile(finalProfile)
    })
}

Ловушки и лучшие практики 🚨

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

Плохой пример ❌:

fetch('/api/data').then(result => {
  result.json().then(data => {
    process(data).then(() => {
      console.log('Готово!')
    })
  })
})

Хороший пример ✅:

fetch('/api/data')
  .then(response => response.json())
  .then(processData)
  .then(() => console.log('Готово!'))
  .catch(handleError)

Когда цепочки — идеальное решение? ✨

Цепочки промисов особенно хороши для:

  • Последовательных асинхронных операций
  • Обработки данных поэтапно
  • Сложной логики обработки ошибок
  • Сценариев, где нужно гарантированно выполнить финальный код

Помните: современный JavaScript предлагает и async/await, но понимание цепочек промисов — это фундамент, без которого не стать настоящим асинхронным ниндзя! 🏼

Скрыть рекламу навсегда

📘 VK Видео — обучение без ограничений

Все уроки доступны без VPN, без блокировок и зависаний.

Можно смотреть с телефона, планшета или компьютера — в любое время.

▶️ Смотреть на VK Видео