PromiseKit原始碼分析

xixinRunBoy發表於2018-03-02

整體思路

鏈式非同步思路 每個Promise中儲存下一個Promise的執行邏輯並把當前操作返回值傳遞給下一個Promise,已經執行的seal狀態變成.resolved,沒有就加到.pendding(handlers)中handlers的bodies裡面等待執行,下一個promise根據上一個promise的state狀態來進行下一步,把自己的處理流程加到上一個promise處理流程的後面

每個promise例項通過state: State<T> 儲存當前promise的狀態

seal表示當前是否已經執行

  • .panding表示沒有執行,正在等待執行
  • .resolved表示已經完成,並且儲存了返回值

fulfill履行

reject拒絕

then成功時呼叫

catch失敗時呼叫

State.swift

兩個重要的列舉

Seal是用來儲存處理狀態

enum Seal<T> {
    case pending(Handlers<T>)  // 等待處理,Handlers用來儲存當前promise下一個處理流程
    case resolved(Resolution<T>)// 已經處理有返回值,Resolution封裝正確和錯誤的返回值
}
複製程式碼

Resolution封裝返回值

enum Resolution<T> {
    case fulfilled(T) // 正確的返回值
    case rejected(Error, ErrorConsumptionToken) // 錯誤的返回值

    init(_ error: Error) {
        self = .rejected(error, ErrorConsumptionToken(error))
    }
}
複製程式碼

Handlers用來儲存當前promise下一個處理流程,bodies儲存的閉包就是接下來要處理的邏輯

class Handlers<T>: Sequence {
    var bodies: [(Resolution<T>) -> Void] = []

    func append(_ body: @escaping (Resolution<T>) -> Void) {
        bodies.append(body)
    }

    func makeIterator() -> IndexingIterator<[(Resolution<T>) -> Void]> {
        return bodies.makeIterator()
    }

    var count: Int {
        return bodies.count
    }
}
複製程式碼

seal屬性:用來儲存處理狀態

seal是在以下閉包中改變

required init(resolver: inout ((Resolution<T>) -> Void)!) {
        seal = .pending(Handlers<T>())
        super.init()
        // 閉包的實現
        resolver = { resolution in
            var handlers: Handlers<T>?
            self.barrier.sync(flags: .barrier) {
                if case .pending(let hh) = self.seal {
                    self.seal = .resolved(resolution)
                    handlers = hh
                }
            }
            if let handlers = handlers {
                for handler in handlers {
                    handler(resolution)
                }
            }
        }
    }
複製程式碼

pipe方法根據seal的狀態來判斷是立即返回還是稍後處理

 final func pipe(_ body: @escaping (Resolution<T>) -> Void) {
        get { seal in
            switch seal {
            case .pending(let handlers):
                handlers.append(body)
            case .resolved(let resolution):
                body(resolution)
            }
        }
    }
複製程式碼

Promise.swift

state: State<T> 儲存當前promise的狀態

初始化方法1,第一個promise初始化呼叫此方法,根據外面呼叫fulfill或者reject,來更新state屬性

required public init(resolvers: (_ fulfill: @escaping (T) -> Void, _ reject: @escaping (Error) -> Void) throws -> Void) {
        var resolve: ((Resolution<T>) -> Void)!
        do {
            state = UnsealedState(resolver: &resolve)
            try resolvers({ resolve(.fulfilled($0)) }, { error in
                #if !PMKDisableWarnings
                    if self.isPending {
                        resolve(Resolution(error))
                    } else {
                        NSLog("PromiseKit: warning: reject called on already rejected Promise: \(error)")
                    }
                #else
                    resolve(Resolution(error))
                #endif
            })
        } catch {
            resolve(Resolution(error))
        }
    }
複製程式碼

初始化方法2,由Promise.then呼叫,在鏈式語法中自動初始化

init(sealant: (@escaping (Resolution<T>) -> Void) -> Void) {
    var resolve: ((Resolution<T>) -> Void)!
    state = UnsealedState(resolver: &resolve)
    sealant(resolve)
}
複製程式碼

then方法,呼叫初始化方法2進行下一個promise的初始化

public func then<U>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> U) -> Promise<U> {
        return Promise<U> { resolve in
            state.then(on: q, else: resolve) { value in
                resolve(.fulfilled(try body(value)))
            }
        }
    }
複製程式碼

程式碼邏輯分析

分析以下程式碼呼叫邏輯

API.getHomeInfo().then { res in
    print(res)
  }.catch { (error) in
    print(error)
}
複製程式碼

API.getHomeInfo()初始化一個promise,state儲存了promise的狀態,當呼叫fulfill或者reject的時候,就會呼叫state中resolver閉包的實現,將seal的狀態從.pending變成.resolved,並將值傳遞給.resolved,將.pending中Handlers取出,將值傳遞給Handlers的bodies閉包陣列來執行

return Promise<T> { fulfill, reject in
  requestByIgnoringCodeEvent(config) { (requestResult) in
    if requestResult.isSuccess {
      fulfill(requestResult)
    } else {
      let message = requestResult.message ?? "伺服器返回錯誤"
      let error = NSError(domain: "Network Error", code: 1,
                userInfo: [NSLocalizedDescriptionKey: message])
      reject(error)
    }
  }
}
複製程式碼

then也是初始化一個promise例項,呼叫初始化方法2,將state中resolve閉包傳遞到外面來根據上一個promise的state.then來進行進行操作

public func then<U>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> U) -> Promise<U> {
        return Promise<U> { resolve in
            state.then(on: q, else: resolve) { value in
                resolve(.fulfilled(try body(value)))
            }
        }
    }
複製程式碼

state.then呼叫的pipe函式

pipe函式呼叫get函式

get函式先判斷seal的狀態,如果當前promise沒有呼叫fullfill或者reject,說明還沒有返回值過來,那seal的狀態就還是.pending就執行緒就等待前面的完成,有返回值seal狀態就變成了.resolved,最後將seal狀態傳遞閉包

pipe函式處理get函式傳遞給來的seal狀態,.pending就接受的閉包新增到handlers的bidies陣列裡面,後面合適的時候在執行閉包,如果是.resolved就直接把.resolved中值傳遞給閉包引數

final func then<U>(on q: DispatchQueue, else rejecter: @escaping (Resolution<U>) -> Void, execute body: @escaping (T) throws -> Void) {
        pipe { resolution in
            switch resolution {
            case .fulfilled(let value):
                contain_zalgo(q, rejecter: rejecter) {
                    // 觸發下個流程的開始
                    try body(value)
                }
            case .rejected(let error, let token):
                rejecter(.rejected(error, token))
            }
        }
}
final func pipe(_ body: @escaping (Resolution<T>) -> Void) {
        get { seal in
            switch seal {
            case .pending(let handlers):
                handlers.append(body)
            case .resolved(let resolution):
                body(resolution)
            }
        }
}
override func get(body: @escaping (Seal<T>) -> Void) {
        var sealed = false
        barrier.sync {
            switch self.seal {
            case .resolved:
                sealed = true
            case .pending:
                sealed = false
            }
        }
        if !sealed {
            barrier.sync(flags: .barrier) {
                switch (self.seal) {
                case .pending:
                    body(self.seal)
                case .resolved:
                    sealed = true  // welcome to race conditions
                }
            }
        }
        if sealed {
            body(seal)  // as much as possible we do things OUTSIDE the barrier_sync
        }
    }
複製程式碼

then函式處理pipe函式傳遞過來的返回值,正常呼叫body(value),異常呼叫rejecter(.rejected(error, token))

promise.then函式處理state.then傳遞過來的引數,在回撥promise的state中初始化的閉包,更新state中seal的狀態

resolver = { resolution in
  var handlers: Handlers<T>?
  self.barrier.sync(flags: .barrier) {
    if case .pending(let hh) = self.seal {
      self.seal = .resolved(resolution)
      handlers = hh
    }
  }
  if let handlers = handlers {
    for handler in handlers {
      handler(resolution)
    }
  }
}
複製程式碼

catch邏輯

promise.catch呼叫state.catch

state.catch呼叫pipe函式

pipe函式呼叫get函式

get函式在進行狀態判斷,返回當前self.seal狀態

pipe函式處理get返回的seal狀態,判斷為.pending,將body新增到handlers的bodies陣列中稍後處理,上一步在更新state中seal狀態時候,會將.pending中handles中的bodies陣列拿出來執行,這個時候就會執行state.catch方法傳入的閉包

promise.catch函式處理state.catch返回的引數,正確就不執行,錯誤執行body閉包,body就是自己寫的錯誤處理邏輯

// Promise中的catch方法
@discardableResult
public func `catch`(on q: DispatchQueue = .default, policy: CatchPolicy = .allErrorsExceptCancellation, execute body: @escaping (Error) -> Void) -> Promise {
  state.catch(on: q, policy: policy, else: { _ in }, execute: body)
        return self
}
// State中的catch方法
final func `catch`(on q: DispatchQueue, policy: CatchPolicy, else resolve: @escaping (Resolution<T>) -> Void, execute body: @escaping (Error) throws -> Void) {
        pipe { resolution in
            switch (resolution, policy) {
            case (.fulfilled, _):
                resolve(resolution)
            case (.rejected(let error, _), .allErrorsExceptCancellation) where error.isCancelledError:
                resolve(resolution)
            case (let .rejected(error, token), _):
                contain_zalgo(q, rejecter: resolve) {
                    token.consumed = true
                    try body(error)
                }
            }
        }
}
複製程式碼

相關文章