PromiKit原理概览

以前咱们说了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等便捷功能

    相关文章
    相关标签/搜索