ݺߣ

ݺߣShare a Scribd company logo
Promises
Поговорим о промисах

• Какие проблемы стояли перед создателями?
• Что это и зачем они нужны?
• Создадим собственные промисы
• Используем их в демо-проекте
• Рассмотрим их преимущества и недостатки
Кто придумал промисы?

Карл Хьюитт
Барбара Лисков
Дэниэл 

Фридман
Проблемы

• Уменьшение задержки
при вычислениях на
удаленных машинах
• Необходимость
максимально
использовать
возможности систем с
множеством процессоров
Идеи

• Распараллелить вычисления
• Переиспользовать получающийся
результат
А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)
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:
Стандартная модель
Background:
Main:
Promise
Future Show Picture
Download
Picture
Picture
Как это могло бы выглядеть в коде?
let downloadPromise = Promise<UIImage>()
let downloadFuture = downloadPromise.future
downloadFuture
.then { image in
self.imageView.image = image
}
.error { err in

…
}
downloadPromise.resolve(image)
Разные реализации в разных языках

• С++: promise + future
• Scala: promise + future
• Javascript: только promise
• Swift: нет в стандартной библиотеке

в PromiseKit - только Promise
Для нашей реализации

Promise == Future
Будем использовать единую сущность
Основные Use-Cases
Последовательные

операции:
Параллельные 

операции:
Record Promise
Bank
Backend
Upload Promise Show alert
Local
Backend
Combined
Promise
Show user
profile
Последовательные операции
func record() -> Promise<RecordSequence>
func upload(sequence: RecordSequence) -> Promise<Void>
record.then(upload).then { // code showing alert }
… а как бы это выглядело с callback’ами
record { recording in
upload(recording) { result, error in
if let result = result { // code showing alert }
// error handling
}
}
Параллельные операции
func bankUserInfo() ->
Promise<BankUserInfoConfiguration>
func localBackendUserInfo() ->
Promise<LocalBackendUserInfoConfiguration>
combine(localBackendUserInfo, bankUserInfo)
.then { local, bank in
…
show(userInfoConfiguration)
}
Создадим собственные
промисы!
Promise
Спецификация
Операции выполняются
сразу же после создания
Можно записать
результат один раз
Ошибка прерывает
выполнение всей
Выполняется на
произвольной очереди
Метод combine и другие
Метод then - цепочка
промисов
В любой
момент можно
получить
доступ к
результату
Под капотом
var result: Result<T>?
var handlers: [PromiseHandler<T>] = []
let queue = DispatchQueue(label: "promise.queue",
attributes: .concurrent)
Конструктор
let promise = Promise<Int> { resolve, reject in
resolve(someValue)
}
Closure, которые дергают внутренние методы Promise
Реализация конструктора
init(closure: PromiseClosure<T>) {
closure(resolve, receivedError)
}
func resolve(_ parameter: T) {
guard result == nil else { return }
result = .resolved(parameter)
handlers.forEach { $0(result!) }
}
Выполняетс
я сразу

после
создания
Метод then
downloadPromise.then { object in
return something(object)
}
нужно продумать несколько вариаций:
например, then принимает другой promise в
качестве параметра
Реализация метода 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
Делегаты - отдельная история

Сложно обернуть в callback
Как нам преобразовать их
в Promise?
Ответ: передаем значение в Promise извне
Делегаты
typealias PromiseTuple<T>
(promise: Promise<T>,
resolve: (T) -> (),
reject : (Error) -> ())
self.delegatePromise = Promise.promiseTuple()
func delegateMethod() {
delegatePromise.resolve(response)
}
Метод combine
combine([promise1, promise2]).then { results in
…
print(results)
}
воспользуемся DispatchGroup
Метод 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
Combine (завершение)
resultGroup.notify(queue: q) {
promiseTuple.resolve(objects)
}
return promiseTuple.promise
Метод combine возвращает promise сразу же,
до завершения вычислений
Иные методы

• when: аналогично combine
• after: DispatchQueue.asyncAfter
• отмена promises == вызов ошибки
Применим знания на
практике!
Use case No. 1

Изменение
положения девайса
Изменение бита
Use case No. 2

Изменение позиции
секвенсора
Изменение UI Проигрывание звука
Use case No.3

Кнопка сохранить
Проигрываем 2 квадрата
Загружаем на сервер
Показываем алерт
Use case No.3

recordingPromise =
sampler.recordBars()
.then(parseService.saveSequences)
.then {
self.view.animateRecord(false)
self.view.showAlert(message:…)
}
.error { … }
ViewModel
Use case No.4

Кнопка загрузить
Загружаем случайный бит
Передаем его в семплер
Показываем алерт
Use case No.4

downloadingPromise =
parseService.loadRandomSequences()
.then(sampler.load)
.then {
self.view.animateDownload(false)
self.view.showAlert(message:…)
}
.error { … }
ViewModel
Use case No.4

ParseService
func loadRandomSequences() -> Promise<[BeatSequence]> {
return client
.downloadRandomFile()
.then(parseData)
}
func downloadRandomFile() -> Promise<Data> {
return allPFObjectsFromServer()
.then(downloadRandomFileFromPFObjects)
}
ParseClient
Когда стоит использовать Promises?

• Сложный бекэнд (последовательные/
параллельные запросы)
• Много трудоемких операций в бекграунд потоке
• Последовательная анимация
• Использование promise в качестве
контейнера
Преимущества Promises

• Декларативно - весь код находится рядом
• Хранит результат для переиспользования
• Легко собирать результаты разных операций
• Удобно обрабатывать ошибки
Недостатки Promises

• Неудобно отлаживать
• Не подходит для непрерывного потока данных
• Неочевидная работа с делегатами
https://github.com/
mcrakhman/drumdemo

More Related Content

Михаил Рахманов — Promises, или почему обещания надо выполнять