Promise & Future 101

Promise & Future 101

技术杂谈小彩虹2021-08-22 19:12:57110A+A-

PromiseFuture 并不特指某种具体的库、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)
}

参考资料

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们