![Page 1: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/1.jpg)
Promises
![Page 2: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/2.jpg)
Поговорим о промисах
• Какие проблемы стояли перед создателями?
• Что это и зачем они нужны?
• Создадим собственные промисы
• Используем их в демо-проекте
• Рассмотрим их преимущества и недостатки
![Page 3: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/3.jpg)
Кто придумал промисы?
Карл Хьюитт
Барбара Лисков
Дэниэл Фридман
![Page 4: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/4.jpg)
Проблемы
• Уменьшение задержки при вычислениях на удаленных машинах
• Необходимость максимально использовать возможности систем с множеством процессоров
![Page 5: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/5.jpg)
Идеи
• Распараллелить вычисления
• Переиспользовать получающийся результат
![Page 6: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/6.jpg)
А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)
![Page 7: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/7.jpg)
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
PrintStudent A
Print Student B
PrintStudent C
DataBase:
![Page 8: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/8.jpg)
Стандартная модель
Background:
Main:
Promise
Future Show Picture
Download Picture
Picture
![Page 9: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/9.jpg)
Как это могло бы выглядеть в коде?
let downloadPromise = Promise<UIImage>() let downloadFuture = downloadPromise.future
downloadFuture .then { image in self.imageView.image = image } .error { err in …
}
downloadPromise.resolve(image)
![Page 10: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/10.jpg)
Разные реализации в разных языках
• С++: promise + future
• Scala: promise + future
• Javascript: только promise
• Swift: нет в стандартной библиотекев PromiseKit - только Promise
![Page 11: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/11.jpg)
Для нашей реализации
Promise == FutureБудем использовать единую сущность
![Page 12: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/12.jpg)
Основные Use-Cases
Последовательныеоперации:
Параллельные операции:
Record Promise
Bank Backend
Upload Promise Show alert
Local Backend
CombinedPromise
Show user profile
![Page 13: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/13.jpg)
Последовательные операции
func record() -> Promise<RecordSequence> func upload(sequence: RecordSequence) -> Promise<Void>
record.then(upload).then { // code showing alert }
![Page 14: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/14.jpg)
… а как бы это выглядело с callback’ами
record { recording in upload(recording) { result, error in
if let result = result { // code showing alert } // error handling }
}
![Page 15: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/15.jpg)
Параллельные операцииfunc bankUserInfo() -> Promise<BankUserInfoConfiguration>
func localBackendUserInfo() -> Promise<LocalBackendUserInfoConfiguration>
combine(localBackendUserInfo, bankUserInfo) .then { local, bank in
… show(userInfoConfiguration) }
![Page 16: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/16.jpg)
Создадим собственные промисы!
![Page 17: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/17.jpg)
Promise
Спецификация
Операции выполняются сразу же после создания
Можно записать результат один раз
Ошибка прерывает выполнение всей
Выполняется на произвольной очереди
Метод combine и другиеМетод then - цепочка промисов
В любой момент можно получить доступ к результату
![Page 18: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/18.jpg)
Под капотом
var result: Result<T>?
var handlers: [PromiseHandler<T>] = []
let queue = DispatchQueue(label: "promise.queue", attributes: .concurrent)
![Page 19: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/19.jpg)
Конструктор
let promise = Promise<Int> { resolve, reject in resolve(someValue)
}
Closure, которые дергают внутренние методы Promise
![Page 20: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/20.jpg)
Реализация конструктора
init(closure: PromiseClosure<T>) { closure(resolve, receivedError) }
func resolve(_ parameter: T) { guard result == nil else { return }
result = .resolved(parameter) handlers.forEach { $0(result!) } }
Выполняется сразу после создания
![Page 21: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/21.jpg)
Метод then
downloadPromise.then { object in return something(object)
}
нужно продумать несколько вариаций:
например, then принимает другой promise в качестве параметра
![Page 22: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/22.jpg)
Реализация метода thenfunc 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
![Page 23: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/23.jpg)
Делегаты - отдельная история
Сложно обернуть в callback
Как нам преобразовать их в Promise?
Ответ: передаем значение в Promise извне
![Page 24: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/24.jpg)
Делегатыtypealias PromiseTuple<T>
(promise: Promise<T>, resolve: (T) -> (), reject : (Error) -> ())
self.delegatePromise = Promise.promiseTuple()
func delegateMethod() { delegatePromise.resolve(response) }
![Page 25: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/25.jpg)
Метод combine
combine([promise1, promise2]).then { results in … print(results) }
воспользуемся DispatchGroup
![Page 26: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/26.jpg)
Метод 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
![Page 27: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/27.jpg)
Combine (завершение)
resultGroup.notify(queue: q) { promiseTuple.resolve(objects) }
return promiseTuple.promise
Метод combine возвращает promise сразу же, до завершения вычислений
![Page 28: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/28.jpg)
Иные методы
• when: аналогично combine
• after: DispatchQueue.asyncAfter
• отмена promises == вызов ошибки
![Page 29: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/29.jpg)
Применим знания на практике!
![Page 30: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/30.jpg)
Use case No. 1
Изменение положения девайса
Изменение бита
![Page 31: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/31.jpg)
Use case No. 2
Изменение позиции секвенсора
Изменение UI Проигрывание звука
![Page 32: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/32.jpg)
Use case No.3
Кнопка сохранить
Проигрываем 2 квадрата
Загружаем на сервер
Показываем алерт
![Page 33: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/33.jpg)
Use case No.3
recordingPromise = sampler.recordBars() .then(parseService.saveSequences) .then { self.view.animateRecord(false) self.view.showAlert(message:…) }
.error { … }
ViewModel
![Page 34: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/34.jpg)
Use case No.4
Кнопка загрузить
Загружаем случайный бит
Передаем его в семплер
Показываем алерт
![Page 35: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/35.jpg)
Use case No.4
downloadingPromise = parseService.loadRandomSequences() .then(sampler.load) .then { self.view.animateDownload(false) self.view.showAlert(message:…) } .error { … }
ViewModel
![Page 36: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/36.jpg)
Use case No.4
ParseServicefunc loadRandomSequences() -> Promise<[BeatSequence]> { return client .downloadRandomFile() .then(parseData) }
func downloadRandomFile() -> Promise<Data> { return allPFObjectsFromServer() .then(downloadRandomFileFromPFObjects) }
ParseClient
![Page 37: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/37.jpg)
Когда стоит использовать Promises?
• Сложный бекэнд (последовательные/параллельные запросы)
• Много трудоемких операций в бекграунд потоке
• Последовательная анимация
• Использование promise в качестве контейнера
![Page 38: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/38.jpg)
Преимущества Promises
• Декларативно - весь код находится рядом
• Хранит результат для переиспользования
• Легко собирать результаты разных операций
• Удобно обрабатывать ошибки
![Page 39: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/39.jpg)
Недостатки Promises
• Неудобно отлаживать
• Не подходит для непрерывного потока данных
• Неочевидная работа с делегатами
![Page 40: Михаил Рахманов — Promises, или почему обещания надо выполнять](https://reader030.vdocuments.net/reader030/viewer/2022020314/5888a4931a28ab264b8b676d/html5/thumbnails/40.jpg)
https://github.com/mcrakhman/drumdemo