Что такое Promise (Обещание) в контексте Swift?

«Что такое Promise (Обещание) в контексте Swift?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Promise (Обещание) — это абстракция для работы с асинхронными операциями, представляющая собой контейнер для значения (или ошибки), которое будет доступно в будущем. Это альтернатива замыканиям (completion handlers) и Combine, популярная благодаря библиотекам вроде PromiseKit или Swift concurrency (async/await).

Основные состояния Promise:

  1. Pending (Ожидание): Исходное состояние, операция выполняется.
  2. Fulfilled (Выполнено): Операция завершилась успешно, и Promise содержит результат.
  3. Rejected (Отклонено): Операция завершилась с ошибкой.

Зачем использовать Promises?

  • Избегание «адской пирамиды замыканий» (Callback Hell): Позволяют выстраивать асинхронные операции в цепочку.
  • Четкая обработка ошибок: Ошибки можно перехватывать в конце цепочки.
  • Улучшенная читаемость: Код выглядит почти как синхронный.

Пример с использованием PromiseKit:

import PromiseKit

// Функция, возвращающая Promise
func fetchUserProfile(from url: URL) -> Promise<UserProfile> {
    return Promise { seal in // `seal` — это объект для завершения Promise
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                seal.reject(error) // 1. Завершаем с ошибкой
                return
            }
            guard let data = data else {
                seal.reject(NetworkError.noData) // 1. Завершаем с кастомной ошибкой
                return
            }
            do {
                let profile = try JSONDecoder().decode(UserProfile.self, from: data)
                seal.fulfill(profile) // 2. Завершаем успешно с результатом
            } catch {
                seal.reject(error) // 1. Завершаем с ошибкой декодирования
            }
        }.resume()
    }
}

// Использование: цепочка асинхронных операций
firstly {
    fetchUserProfile(from: userURL) // Загружаем профиль
}.then { profile in
    cacheProfile(profile) // Кэшируем его (возвращает Promise<Void>)
}.then { _ in
    updateUI(with: profile) // Обновляем интерфейс
}.catch { error in
    // Единая точка обработки ЛЮБОЙ ошибки из всей цепочки
    print("Ошибка в цепочке: (error)")
}.finally {
    // Код, который выполнится в любом случае (успех или ошибка)
    hideLoadingIndicator()
}

Связь с современным Swift: Нативная поддержка асинхронности в Swift (async/await) решает те же проблемы, что и Promise. Библиотеки PromiseKit можно использовать совместно с async/await через методы .value.

// Использование Promise с async/await
func loadData() async {
    do {
        let profile = try await fetchUserProfile(from: url).value // Ожидание Promise
        await updateUI(with: profile)
    } catch {
        print(error)
    }
}