Михаил Рахманов — promises, или почему обещания надо...

40
Promises

Upload: cocoaheads

Post on 25-Jan-2017

74 views

Category:

Mobile


1 download

TRANSCRIPT

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

Promises

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

Поговорим о промисах

• Какие проблемы стояли перед создателями?

• Что это и зачем они нужны?

• Создадим собственные промисы

• Используем их в демо-проекте

• Рассмотрим их преимущества и недостатки

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

Кто придумал промисы?

Карл Хьюитт

Барбара Лисков

Дэниэл Фридман

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

Проблемы

• Уменьшение задержки при вычислениях на удаленных машинах

• Необходимость максимально использовать возможности систем с множеством процессоров

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

Идеи

• Распараллелить вычисления

• Переиспользовать получающийся результат

Page 6: Михаил Рахманов — 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)

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

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, или почему обещания надо выполнять

Стандартная модель

Background:

Main:

Promise

Future Show Picture

Download Picture

Picture

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

Как это могло бы выглядеть в коде?

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, или почему обещания надо выполнять

Разные реализации в разных языках

• С++: promise + future

• Scala: promise + future

• Javascript: только promise

• Swift: нет в стандартной библиотекев PromiseKit - только Promise

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

Для нашей реализации

Promise == FutureБудем использовать единую сущность

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

Основные Use-Cases

Последовательныеоперации:

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

Record Promise

Bank Backend

Upload Promise Show alert

Local Backend

CombinedPromise

Show user profile

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

Последовательные операции

func record() -> Promise<RecordSequence> func upload(sequence: RecordSequence) -> Promise<Void>

record.then(upload).then { // code showing alert }

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

… а как бы это выглядело с callback’ами

record { recording in upload(recording) { result, error in

if let result = result { // code showing alert } // error handling }

}

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

Параллельные операцииfunc bankUserInfo() -> Promise<BankUserInfoConfiguration>

func localBackendUserInfo() -> Promise<LocalBackendUserInfoConfiguration>

combine(localBackendUserInfo, bankUserInfo) .then { local, bank in

… show(userInfoConfiguration) }

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

Создадим собственные промисы!

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

Promise

Спецификация

Операции выполняются сразу же после создания

Можно записать результат один раз

Ошибка прерывает выполнение всей

Выполняется на произвольной очереди

Метод combine и другиеМетод then - цепочка промисов

В любой момент можно получить доступ к результату

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

Под капотом

var result: Result<T>?

var handlers: [PromiseHandler<T>] = []

let queue = DispatchQueue(label: "promise.queue", attributes: .concurrent)

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

Конструктор

let promise = Promise<Int> { resolve, reject in resolve(someValue)

}

Closure, которые дергают внутренние методы Promise

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

Реализация конструктора

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, или почему обещания надо выполнять

Метод then

downloadPromise.then { object in return something(object)

}

нужно продумать несколько вариаций:

например, then принимает другой promise в качестве параметра

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

Реализация метода 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, или почему обещания надо выполнять

Делегаты - отдельная история

Сложно обернуть в callback

Как нам преобразовать их в Promise?

Ответ: передаем значение в Promise извне

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

Делегатыtypealias PromiseTuple<T>

(promise: Promise<T>, resolve: (T) -> (), reject : (Error) -> ())

self.delegatePromise = Promise.promiseTuple()

func delegateMethod() { delegatePromise.resolve(response) }

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

Метод combine

combine([promise1, promise2]).then { results in … print(results) }

воспользуемся DispatchGroup

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

Метод 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, или почему обещания надо выполнять

Combine (завершение)

resultGroup.notify(queue: q) { promiseTuple.resolve(objects) }

return promiseTuple.promise

Метод combine возвращает promise сразу же, до завершения вычислений

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

Иные методы

• when: аналогично combine

• after: DispatchQueue.asyncAfter

• отмена promises == вызов ошибки

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

Применим знания на практике!

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

Use case No. 1

Изменение положения девайса

Изменение бита

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

Use case No. 2

Изменение позиции секвенсора

Изменение UI Проигрывание звука

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

Use case No.3

Кнопка сохранить

Проигрываем 2 квадрата

Загружаем на сервер

Показываем алерт

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

Use case No.3

recordingPromise = sampler.recordBars() .then(parseService.saveSequences) .then { self.view.animateRecord(false) self.view.showAlert(message:…) }

.error { … }

ViewModel

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

Use case No.4

Кнопка загрузить

Загружаем случайный бит

Передаем его в семплер

Показываем алерт

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

Use case No.4

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

ViewModel

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

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, или почему обещания надо выполнять

Когда стоит использовать Promises?

• Сложный бекэнд (последовательные/параллельные запросы)

• Много трудоемких операций в бекграунд потоке

• Последовательная анимация

• Использование promise в качестве контейнера

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

Преимущества Promises

• Декларативно - весь код находится рядом

• Хранит результат для переиспользования

• Легко собирать результаты разных операций

• Удобно обрабатывать ошибки

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

Недостатки Promises

• Неудобно отлаживать

• Не подходит для непрерывного потока данных

• Неочевидная работа с делегатами