以前咱們說了PromiseKit今天就帶領你們來分析一下這個PromiseKit,以後咱們再根據PromiseKit的思想剝繭抽絲的簡易的擼一個微型的PromiseKit。swift
Promise的繼承關係以下圖所示api
從上圖建議的大綱預覽咱們能夠看到Promise繼承於Thenable這個protocol而總體的核心思想在Thenable中,Promise只是對這個思想進行了擴展,固然了假如說swift中protocol可以進行實例化的話Promise徹底就不必存在啦…
promise
看完了基本的主線關係圖咱們來講說PromiseKit的核心思想--裝箱和開箱。Promise認爲全部的數據都須要先裝箱而後開箱,採用層級傳遞的方式,將數據進行一次裝箱封裝傳遞出去而在裝箱過程當中數據是能夠被加工的的。而對於下一級的流程會告訴你上一級我須要什麼樣的數據而上一級會須要根據下一級以及上一級的數據進行轉化以知足下一級的需求。所以能夠認爲任何一級都須要對上指定接收格式,對下進行格式轉換。安全
說到了裝箱咱們來看看出現的關鍵環節Boxbash
/// - Remark: not protocol ∵ http://www.russbishop.net/swift-associated-types-contclass Box<T> { func inspect() -> Sealant<T> { fatalError() } func inspect(_: (Sealant<T>) -> Void) { fatalError() } func seal(_: T) {}}final class SealedBox<T>: Box<T> { let value: T
init(value: T) { self.value = value } override func inspect() -> Sealant<T> { return .resolved(value) }}複製代碼
這個Box是一個簡易的抽象類,定義了數據的裝箱和取數據行爲,而更加具體的功能則在子類中,如上SealedBox只是簡單的進行了數據的存儲功能,更加具體則在下面的子類中:
多線程
class EmptyBox<T>: Box<T> { private var sealant = Sealant<T>.pending(.init()) private let barrier = DispatchQueue(label: "org.promisekit.barrier", attributes: .concurrent)
override func seal(_ value: T) { var handlers: Handlers<T>! barrier.sync(flags: .barrier) { guard case .pending(let _handlers) = self.sealant else { return // already fulfilled! } handlers = _handlers self.sealant = .resolved(value) }
//FIXME we are resolved so should `pipe(to:)` be called at this instant, 「thens are called in order」 would be invalid //NOTE we don’t do this in the above `sync` because that could potentially deadlock //THOUGH since `then` etc. typically invoke after a run-loop cycle, this issue is somewhat less severe
if let handlers = handlers { handlers.bodies.forEach{ $0(value) } }
//TODO solution is an unfortunate third state 「sealed」 where then's get added // to a separate handler pool for that state // any other solution has potential races } override func inspect() -> Sealant<T> { var rv: Sealant<T>! barrier.sync { rv = self.sealant } return rv } override func inspect(_ body: (Sealant<T>) -> Void) { var sealed = false barrier.sync(flags: .barrier) { switch sealant { case .pending: // body will append to handlers, so we must stay barrier’d body(sealant) case .resolved: sealed = true } } if sealed { // we do this outside the barrier to prevent potential deadlocks // it's safe because we never transition away from this state body(sealant) } }}複製代碼
EmptyBox可謂是整個裝箱開箱的主體,實現裝箱開箱完的所有功能。咱們就一點點的屢一下思路……閉包
先從成員變量提及:app
sealant:默認數據是混沌的須要等待基於一個數據複製代碼
barrier:是GCD中的知識,意爲柵欄,須要等待當前以及以上代碼執行完纔可執行下面流程(保證數據的多線程下安全)less
再來看看行爲:async
override func seal(_ value: T) 的目的很簡單就是將數據封裝給已有的handler具體的怎麼使用後續咱們會舉例
override func inspect() -> Sealant<T>和複製代碼
override func inspect(_ body: (Sealant<T>) -> Void)複製代碼
只是簡單取出Box中數據展現給別人看…
箱子看完了,咱們看看怎麼實現裝箱和拆箱吧:Promise
public enum Result<T> { case fulfilled(T) case rejected(Error)}
public final class Promise<T>: Thenable, CatchMixin { let box: Box<Result<T>>
fileprivate init(box: SealedBox<Result<T>>) { self.box = box }
/** Initialize a new fulfilled promise.
We do not provide `init(value:)` because Swift is 「greedy」 and would pick that initializer in cases where it should pick one of the other more specific options leading to Promises with `T` that is eg: `Error` or worse `(T->Void,Error->Void)` for uses of our PMK < 4 pending initializer due to Swift trailing closure syntax (nothing good comes without pain!).
Though often easy to detect, sometimes these issues would be hidden by other type inference leading to some nasty bugs in production.
In PMK5 we tried to work around this by making the pending initializer take the form `Promise(.pending)` but this led to bad migration errors for PMK4 users. Hence instead we quickly released PMK6 and now only provide this initializer for making sealed & fulfilled promises.
Usage is still (usually) good:
guard foo else { return .value(bar) } */ public class func value(_ value: T) -> Promise<T> { return Promise(box: SealedBox(value: .fulfilled(value))) }
/// Initialize a new rejected promise. public init(error: Error) { box = SealedBox(value: .rejected(error)) }
/// Initialize a new promise bound to the provided `Thenable`. public init<U: Thenable>(_ bridge: U) where U.T == T { box = EmptyBox() bridge.pipe(to: box.seal) }
/// Initialize a new promise that can be resolved with the provided `Resolver`. public init(resolver body: (Resolver<T>) throws -> Void) { box = EmptyBox() let resolver = Resolver(box) do { try body(resolver) } catch { resolver.reject(error) } }
/// - Returns: a tuple of a new pending promise and its `Resolver`. public class func pending() -> (promise: Promise<T>, resolver: Resolver<T>) { return { ($0, Resolver($0.box)) }(Promise<T>(.pending)) }
/// - See: `Thenable.pipe` public func pipe(to: @escaping(Result<T>) -> Void) { switch box.inspect() { case .pending: box.inspect { switch $0 { case .pending(let handlers): handlers.append(to) case .resolved(let value): to(value) } } case .resolved(let value): to(value) } }
/// - See: `Thenable.result` public var result: Result<T>? { switch box.inspect() { case .pending: return nil case .resolved(let result): return result } }
init(_: PMKUnambiguousInitializer) { box = EmptyBox() }}複製代碼
從代碼咱們看到Promise是一個final類型的class不可進行繼承,而內部
let box: Box<Result<T>>Box存儲的是一個enum的數據(包含正常和error。總體的數定義十分的簡單就是一些初始化。而關鍵的位置在於
public func pipe(to: @escaping(Result<T>) -> Void) 公有兩個做用 1 將正常的數據經過閉包傳遞出去共外部使用 2 自身混沌的數據再次裝箱給handler以便後續對數據處理
下面咱們來看看Thenable這個根源怎麼玩的
func then<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise<U.T> { let rp = Promise<U.T>(.pending) pipe { switch $0 { case .fulfilled(let value): on.async(flags: flags) { do { let rv = try body(value) guard rv !== rp else { throw PMKError.returnedSelf } rv.pipe(to: rp.box.seal) } catch { rp.box.seal(.rejected(error)) } } case .rejected(let error): rp.box.seal(.rejected(error)) } } return rp }複製代碼
then的功能很簡單就是起到一個緩和緩衝的目的,就比如說話時候的簡單停頓同樣,其目的並非作數據和邏輯轉化只是簡單將數據本來不變的傳給下一級
例如:
firstly { URLSession.shared.dataTask(.promise, with: url1) }.then { response in transform(data: response.data) }.done { transformation in //… }複製代碼
其他的功能你們根據Thenable的源碼本身分析,大致功能就是數據轉換,降緯等譯增長便捷性……將層的數據回調變爲一級級的數據傳遞……
後續咱們會慢慢分析其他的簡便功能:race after when等便捷功能