PromiseKit 源码初探(一)

规范

Promise/A+

PromiseKit - Swift 实现

Box and Result

Promise 最终的结果一定反映出两个状态,所以 PromisKit 定义了以下的 Result 枚举

1
2
3
4
public enum Result<T> {
case fulfilled(T)
case rejected(Error)
}

Result 类型一定要把它看作最终的结果。

Promise 自身还有两个状态,pending(等待执行) 以及 resolved(执行完毕)
虽然在 PromiseKit 中有一个基类 Box 作为非正式协议使用

1
2
3
4
5
class Box<T> {
func inspect() -> Sealant<T> { fatalError() }
func inspect(_: (Sealant<T>) -> Void) { fatalError() }
func seal(_: T) {}// 此函数执行以后会使内部的状态发生改变,即由 pending 态转换为 resolve 态
}

Box 中装了一个内部的类型 Sealant

1
2
3
4
5
6
7
8
9
enum Sealant<R> {
case pending(Handlers<R>)
case resolved(R)
}
class Handlers<R> {
var bodies: [(R) -> Void] = []
func append(_ item: @escaping(R) -> Void) { bodies.append(item) }
}

Handlers 包装的是一些内部使用的函数,内部逻辑基本上是将当 then 中返回的 Promise 的状态传递到 then 返回的 Promise 中,见规范 2.3

Resolver

Resolver 是作为提供给 Promise 包装函数使用,用于触发 Promise 的工具,它基本提供的方法如下

1
2
3
4
5
6
class Resolver<T> {
let box: Box<Result<T>>
init(_ box: Box<Result<T>>) { self.box = box }
func fulfill(_ value: T) { box.seal(.fulfilled(value)) }
func reject(_ error:Error) { box.seal(.reject(error)) }
}

fulfill 当包装函数执行成功时调用
reject 当保障函数执行失败时调用

Thenable

“thenable” is an object or function that defines a then method.

PromiseKit 定义了如下接口

1
2
3
4
5
6
7
8
9
10
public protocol Thenable: class {
/// The type of the wrapped value
associatedtype T
/// `pipe` is immediately executed when this `Thenable` is resolved
func pipe(to: @escaping(Result<T>) -> Void)
/// The resolved result or nil if pending.
var result: Result<T>? { get }
}

pipe 将传入的函数放入到待执行队列中。如果 Promise 已经完成,则立即执行传入的函数。

PromiseKit 提供了一下 then 方法的默认实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
}

其中 fulfill 的情况很有意思,摘录出来

1
2
3
let rv = try body(value)
guard rv !== rp else { throw PMKError.returnedSelf }
rv.pipe(to: rp.box.seal)

其中它调用了 body 满足了立即执行的要求,检查 rvrp 是符合以下规则

If promise and x refer to the same object, reject promise with a TypeError as the reason.

rv.pipe(to: rp.box.seal) 则巧妙的实现了 2.3.2.1

If x is pending, promise must remain pending until x is fulfilled or rejected

Promise

一下是 PromiseKit 实现的简单版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Promise<T>: Thenable {
var box:Box<Result<T>>
init() { box = EmptyBox() }
init(resolver body: (Resolver<T>) throws -> Void) {
box = EmptyBox()
let resolver = Resolver<T>(box)
do {
try body(resolver)
} catch {
resolver.reject(error)
}
}
func pip(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)
}
}
}

核心的 pip 函数完成 Handler 的接驳工作,逻辑就是检查 box 中的内容是否是 pending 是则将接驳函数放入队列中;否则就立即执行,把结果给出去。

总览

综合看 PromiseKit 的类名很有意思,但是它提供了一个很好的比喻。

每一个 Promise 都有一个 Box 来存储当前的状态以及被注册的 Handler。比如几个 Promise 用链式调用 then 方法可以得到如下的结果。pipe() 函数的公用就是连接两个 Promisebox, 同时 pipe() 也是一个开关,当 Box 没有打开的时候是不通的;当 Box seal() 的时候,Box 把其内容放出,下一个 box 中.

graph TD
B1[Box1]  -."P1.then()".->  B2[Box2]
B1 --"P1.pip()"--> Body(body)
Body -."body()".-> B3[Box3]
B3 --"P1.pip(to)"--> B2

上图展示了一个 Promise 调用一次 then 产生的几个 Promise的关系,实线表示 Box 的数据流向,虚线表示 Promise 产生的关系。
Box 释放出来的数据进入 body(),它产生了一个新的 Promise ,然后用关键的一句

1
rv.pipe(to: rp.box.seal)

P3 来控制 P2 Box2 的释放,并将得到的数据传递给 Box2 ,让 Box2 seal 出去。

总结

看完感觉有很多可以学习的地方。

  • enum 的使用,完成了状态和值的绑定。
  • Boxpipe 的抽象很让人很容易理解
  • extension protocol 的使用,给 protocol 一个默认的实现,让拓展 protocol 更加方便,提供更便捷的 protocol

##参考资料