2. Поговорим о промисах
• Какие проблемы стояли перед создателями?
• Что это и зачем они нужны?
• Создадим собственные промисы
• Используем их в демо-проекте
• Рассмотрим их преимущества и недостатки
4. Проблемы
• Уменьшение задержки
при вычислениях на
удаленных машинах
• Необходимость
максимально
использовать
возможности систем с
множеством процессоров
6. АProcess 1: B+
АProcess 2: C+
Process 3:
Future
A
Future
A
Future
B
Future
C
Process Queue Result
Future A
Result
A
Result
A
Futures
(H. Baker, C. Hewitt)
7. Fork 1:
Fork 2:
Queue:
Promises
(B. Liskov, L. Shira)
Record
Grade
Record
Grade
Record
Grade
Promise
Student A
Promise
Student B
Promise
Student C
Print
Student A
Print
Student B
Print
Student C
DataBase:
9. Как это могло бы выглядеть в коде?
let downloadPromise = Promise<UIImage>()
let downloadFuture = downloadPromise.future
downloadFuture
.then { image in
self.imageView.image = image
}
.error { err in
…
}
downloadPromise.resolve(image)
10. Разные реализации в разных языках
• С++: promise + future
• Scala: promise + future
• Javascript: только promise
• Swift: нет в стандартной библиотеке
в PromiseKit - только Promise
14. … а как бы это выглядело с callback’ами
record { recording in
upload(recording) { result, error in
if let result = result { // code showing alert }
// error handling
}
}
15. Параллельные операции
func bankUserInfo() ->
Promise<BankUserInfoConfiguration>
func localBackendUserInfo() ->
Promise<LocalBackendUserInfoConfiguration>
combine(localBackendUserInfo, bankUserInfo)
.then { local, bank in
…
show(userInfoConfiguration)
}
17. Promise
Спецификация
Операции выполняются
сразу же после создания
Можно записать
результат один раз
Ошибка прерывает
выполнение всей
Выполняется на
произвольной очереди
Метод combine и другие
Метод then - цепочка
промисов
В любой
момент можно
получить
доступ к
результату
18. Под капотом
var result: Result<T>?
var handlers: [PromiseHandler<T>] = []
let queue = DispatchQueue(label: "promise.queue",
attributes: .concurrent)
19. Конструктор
let promise = Promise<Int> { resolve, reject in
resolve(someValue)
}
Closure, которые дергают внутренние методы Promise
20. Реализация конструктора
init(closure: PromiseClosure<T>) {
closure(resolve, receivedError)
}
func resolve(_ parameter: T) {
guard result == nil else { return }
result = .resolved(parameter)
handlers.forEach { $0(result!) }
}
Выполняетс
я сразу
после
создания
21. Метод then
downloadPromise.then { object in
return something(object)
}
нужно продумать несколько вариаций:
например, then принимает другой promise в
качестве параметра
22. Реализация метода then
func then(onQueue q: DispatchQueue, closure: (T) throws -> U)
-> Promise<U> {
}
return Promise<U> { resolve, reject in
}
self.addHandler { result in
q.async {
switch result {
case .resolved(let parameter):
resolve(try closure(parameter))
case .rejected(let error):
reject(error)
}
}
}
Аналогичная реализация
метода error
23. Делегаты - отдельная история
Сложно обернуть в callback
Как нам преобразовать их
в Promise?
Ответ: передаем значение в Promise извне
26. Метод combine
let resultGroup = DispatchGroup()
var objects: [T] = []
let promiseTuple = Promise<[T]>.promiseTuple()
resultGroup.enter()
promise.then { value in
resultGroup.leave()
objects.append(value)
...
}
.error { err in
promiseTuple.reject(err)
}
func combine<T>(q: DispatchQueue,
promises: [Promise<T>]) -> Promise<[T]>
Для каждого promise
37. Когда стоит использовать Promises?
• Сложный бекэнд (последовательные/
параллельные запросы)
• Много трудоемких операций в бекграунд потоке
• Последовательная анимация
• Использование promise в качестве
контейнера
38. Преимущества Promises
• Декларативно - весь код находится рядом
• Хранит результат для переиспользования
• Легко собирать результаты разных операций
• Удобно обрабатывать ошибки
39. Недостатки Promises
• Неудобно отлаживать
• Не подходит для непрерывного потока данных
• Неочевидная работа с делегатами