Future и Promise — это два взаимосвязанных паттерна для работы с асинхронными операциями. Они представляют собой абстракцию над значением, которое может быть доступно сейчас или в будущем.
import Combine
func fetchUser(id: Int) -> Future<User, Error> {
return Future { promise in
URLSession.shared.dataTask(with: userURL(for: id)) { data, _, error in
if let error = error {
promise(.failure(error))
} else if let data = data {
do {
let user = try JSONDecoder().decode(User.self, from: data)
promise(.success(user))
} catch {
promise(.failure(error))
}
}
}.resume()
}
}
// Использование
var cancellables = Set<AnyCancellable>()
fetchUser(id: 123)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error): print("Error: \(error)")
}
}, receiveValue: { user in
print("Received user: \(user)")
})
.store(in: &cancellables)
import PromiseKit
func fetchAvatar(userId: Int) -> Promise<UIImage> {
return Promise { seal in
firstly {
fetchUser(id: userId)
}.then { user in
URLSession.shared.dataTask(.promise, with: user.avatarURL)
}.done { data in
if let image = UIImage(data: data) {
seal.fulfill(image)
} else {
seal.reject(AvatarError.invalidImage)
}
}.catch { error in
seal.reject(error)
}
}
}
Избегание callback hell: Плохо:
loadData { data in
parse(data) { result in
save(result) { success in
// Уже 3 уровня вложенности
}
}
}
Хорошо:
loadData()
.flatMap(parse)
.flatMap(save)
.sink { /* единый обработчик */ }
Композиция операций:
Читаемость кода:
Характеристика | Promise | Future |
---|---|---|
Ответственность | Создание результата | Подписка на результат |
Интерфейс | Завершение операции | Чтение результата |
Мутабельность | Мутабельный (один раз) | Иммутабельный |
Аналог | Producer | Consumer |
Сетевые запросы:
Future<Data, Error> { promise in
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
promise(.failure(error))
} else if let data = data {
promise(.success(data))
}
}.resume()
}
Базы данных:
func saveToCoreData(object: Model) -> Future<Void, Error> {
Future { promise in
context.perform {
do {
try context.save()
promise(.success(()))
} catch {
promise(.failure(error))
}
}
}
}
UserDefaults/Keychain:
func loadSettings() -> Future<Settings, Error> {
Future { promise in
// Асинхронная загрузка
}
}
Преобразование ошибок:
fetchUser(id: 123)
.mapError { networkError in
return MyAppError.network(networkError)
}
Повторные попытки:
fetchUser(id: 123)
.retry(3)
.sink { /* ... */ }
Fallback значения:
fetchUser(id: 123)
.replaceError(with: User.anonymous)
.sink { /* ... */ }
fetchUser(id: 123)
.flatMap { user in
fetchAvatar(for: user)
}
.sink { avatar in
// Обработка аватара
}
Publishers.Zip(
fetchUserProfile(),
fetchUserSettings()
).sink { profile, settings in
// Оба значения получены
}
Важно правильно управлять жизненным циклом:
class ViewController: UIViewController {
private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
.sink { data in
// Обработка
}
.store(in: &cancellables) // Автоматическая отмена при deinit
}
}
Future/Promise — это мощные абстракции для работы с асинхронным кодом в iOS, которые:
Основные реализации в iOS:
Ключевые моменты:
Использование Future/Promise делает асинхронный код более предсказуемым и поддерживаемым в iOS приложениях.