源码版本:6.8.4
首先从官方实例开始
1 | import PromiseKit |
firstly
是一个全局函数
1 | public func firstly<U: Thenable>(execute body: () throws -> U) -> Promise<U.T> { |
接受一个返回Thenable
的闭包,返回一个Promise<U.T>
对于官方实例返回的就是Promise<Int>
Promise
1 | public final class Promise<T>: Thenable, CatchMixin { |
关于Promise
先看这个几个方法,首先内部维护了一个Box
范型对象,构造器或者类方法都会对box
属性赋值,但是传递的参数不同,下面看看Box
是什么。
Box
1 | class Box<T> { |
这个类可以理解为一个抽象类,由于Swift不支持抽象类,所以用这种抛出错误的方式强制用户不可以直接调用,让子类重写。(PS:为啥不用协议呢?~不理解)看看子类实现:
1 | final class SealedBox<T>: Box<T> { |
看类的命名是一个密封了的盒子,就是已经得到确定值的Box
,inspect
方法返回一个Enum
类型携带了这个值
1 | enum Sealant<R> { |
另一个子类实现:
1 | final class Handlers<R> { |
结合pipe
方法来分析一下inspect
到底做了什么,pipe
方法如下:
1 | /// - See: `Thenable.pipe` |
pipe
调用inspect
传递了一个闭包,我们一层一层展开它就变成这个样子
1 | override func inspect(_ body: (Sealant<T>) -> Void) { |
firstly执行流程总结
首先fristly
全局方法会将我们传递进去的Promise
调用pipe
连接到一个pending
的Promise
,由于pipe
接收一个闭包参数,正好Box
类内部的func seal(_: T) {}
就符合这个闭包,所以传递pipe
就接受了seal
作为参数,所以到目前为止
1 | firstly { |
可以转换成下面这样
1 | Promise.value(1).pipe(to:EmptyBox().seal) |
Promise.value(1)
创建的Promise
内部会维护一个resolved
类型的Box
,接着EmptyBox()
创建了一个pending
类型的Box
,pipe
方法内部会调用pending
类型Box
的seal
方法将Promise.value(1)
内部的值封装进EmptyBox()
中,并且改变Box
的类型为resolved
,然后返回了这个新的盒子。
短短一句firstly
包含了一个盒子的替换和状态改变。
map操作
1 | func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise<U> { |
首先上一步的Promise.value(1)
就会创建一个Promise(box: SealedBox(value: .fulfilled(value)))
,内部Box
中的值就是fulfilled
类型的,所以这里的pipe
会执行fulfilled
分支,在制定的线程上异步执行内部新盒子的seal
方法替换值并改变状态,这里内部还会捕捉transform
执行的异常自动的进行rejected
调用。如果没有错误,就又会返回一个新的Promise
包含了map
之后的新值。
then操作
1 | func then<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise<U.T> { |
看这个样子有点像map
操作,但是仔细看返回值不一样,map
操作返回Promise<U>
,then
操作返回Promise<U.T>
,这是因为我们在map
和then
中做的事情不一样,比如我们的Promise<Int>
做map
操作,transform
闭包返回值是Int
,而then
中body
闭包返回值是Promise<T>
,所以最后还要将内部的类型取出来作为一个新得Promise
返回,所以就要返回Promise<U.T>
。
剩下的操作差不多就是在指定的线程队列上异步指定body
取得新的Promise
,然后判断我们传递进去的Promise
和内部新创建的Promise
是不是相同,如果相同就抛出错误,如果不同就我们传入的Promise
值传递给内部Promise
并改变状态,最后返回新Promise
。
done操作
1 | func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> Promise<Void> { |
看到这个指定逻辑就知道了,已经到最后的执行步骤了,不会在有下一步的执行,所以传递进去的闭包没有返回值,内部也直接返回了一个Promise<Void>
。
catch操作
1 | @discardableResult |
PMKFinalizer类型
1 | public class PMKFinalizer { |
这个第一句有点意思 它的方法像下面这样
1 | //方法内部又是一个方法,内部方法返回的是一个元组 所以padding的类型就是一个元组 |
最后padding的类型是:
1 | let pending = (Guarantee<T>(.pending), Guarantee<T>(.pending).box.seal) |
所以再回过头看catch
的操作就是,检查前面的步骤中有rejected
,就执行rejected
操作,进行一个错误的筛选,然后执行我们的闭包操作在执行finalizer.pending.resolve(())
,其实这个finalizer.pending.resolve(())
具体的执行仅仅改变了Guarantee
内部box
的状态剩下的啥也没干,筛选错误时还允许穿透到fulfilled
分支,其实也是什么也没做,仅仅改变状态,最后返回finalizer
对象。
finally操作
因为catch
操作返回的finalizer
对象有一个finally
方法,所以最后可以调用,内部调用了元组中Guarantee<T>(.pending)
的done
方法
1 | @discardableResult |
Guarantee
中的done
方法没有rejected
分支判断,这个类的语义就是保证都成功,其他的和Promise
的done
操作类似。
最后的总结
首先我们构造Promise
时
1 | firstly { |
firstly
内部帮我们启动了一个异步队列跑我们的耗时任务,然后在接下来的map
或then
中又切换回主线程调用我们的操作,这样就实现了异步的链式调用。