Promise 和 Future 并不特指某种具体的库、SDK、框架,它们是一种构念/设计模式 (constructs),为了我们能更方便地去进行异步/并发编程。
在通常采用回调方式的异步编程中,我们主要面临两个问题:1) 末日金字塔 (callbacks pyramid of doom) ;2) 控制流地狱 (control flow hell) 。比如下面这样的代码:
submitForm(func() {
validate()
callRemoteAPI(func() {
if (error) {
prompt(func() {
retry(..)
})
} else {
save(func() {
prompt(func() {
goback()
})
})
}
})
})
复制代码
而 Promise/Future 带来的帮助是可以简化异步代码,从而避免了回调的嵌套,同时还可以去掉重复代码,把异常逻辑放在一起集中处理。比如上面的代码可以简化为:
firstly {
validate()
}.then {
callRemoteAPI()
}.then {
save()
}.then {
prompt()
}.done {
goback()
}.catch {
deal(error)
}
复制代码
那到底什么是 Promise 和 Future 呢?它们的区别又是什么呢?用通俗一点的话讲:Promise (承诺) 就是你对别人做出的一个承诺,同时由你决定是否遵守承诺;而当别人给我一个承诺时,我就必须等着看对方在 Future (未来) 是否兑现它。其实它们是同一个硬币的两面,是从不同视角看去的同一个概念。
从技术上讲,它们的区别在于,Future 是一个只读的占位符,它的结果是由别人设置的;而 Promise 是一个可写的占位符,其结果是可以被你去设置的 (实际上是可以被任何人设置) 。它们实现上可以是不同的对象,但在很多实践里,会把 Promise 作为对 Future 的一个扩展来实现。
那么 Promise 和 Future 具体是怎么实现的呢?这里示例一个最简版本的实现:
class Future<Value> {
var result: Result {
didSet { callbacks.forEach { $0(result) } }
}
func observe(using callback: @escaping (Result) -> Void) {
if let result = result {
return callback(result)
}
callbacks.append(callback)
}
}
class Promise<Value>: Future<Value> {
func resolve(with value: Value) {
result = .success(value)
}
func reject(with error: Error) {
result = .failure(error)
}
}
复制代码
可以看到,当 Future 获得结果 (Result) 后,就会通知所有订阅它的回调,但它的结果是只读的; Promise 则继承了 Future,并提供了设置结果的方法。下面我们看一下如何把一个异步代码变成 Promise/Future 的形式:
func callRemoteAPI() -> Future<Data> {
let promise = Promise<Data>()
httpClient.post() { data, _, error in
if let error = error {
promise.reject(with: error)
} else {
promise.resolve(with: data ?? Data())
}
}
return promise
}
复制代码
可以看到,通过创建一个 Promise 实例,在正常调用异步的方法后,给 promise 设置不同的结果就完成了整个包装的过程,然后以 Future 的形式返回这个 promise 就可以让外部使用了。最后我们看一下调用方的代码:
let future = self.callRemoteAPI()
.decoded()
.save()
future.observe { result in
handle(result)
}
复制代码