Futures/Promises Golang方式实现

来源:互联网 发布:安卓拼图游戏源码 编辑:程序博客网 时间:2024/06/06 00:32

原文:http://labs.strava.com/blog/futures-in-golang/

我并没有打算逐字逐句的翻译,我觉得太死板, 所以我把核心大意说一下就好,如果您希望逐字逐句斟酌, 请看原文链接!

通常网络程序中, 大量时间花费在请求和响应上, 如果需要发送的请求比较多, 若以同步方式,则必然是发送请求,等待响应;

然后再发下一个请求并等待结果返回, 这样依次处理,其效率可想而知, 比较低下!若以异步方式,则可将所有请求并行发出,

而每一个请求的响应则以回调方式异步处理,这样效率自然是非常高,但是异步回调方式不直观,并碎片化业务代码!所以比较好

的方案就是: 代码形式是同步的,执行实质是异步的,当属:Futures/Promises, 其它语言早已有之。 


见go代码:


package mainimport (    "io/ioutil"    "log"    "net/http")func RequestFuture(url string) <-chan []byte {    c := make(chan []byte, 1)    go func() {        var body []byte        defer func() {             c <- body        }()        resp, err := http.Get(url)        if err != nil {            return        }        defer resp.Body.Close()        body, _ = ioutil.ReadAll(resp.Body) //同步阻塞调用,直到响应返回。    }()    return c}func main() {    future := RequestFuture("http://labs.strava.com")    // do many other things, maybe create other futures    body := <-future    log.Printf("response length: %d", len(body))}
以上代码少而直观,不需多言,其缺点为:出错时channel只会返加nil/empty, 调用者拿不掉错误信息, 所以需改进。

见go代码:

unc RequestFutureFunction(url string) func() ([]byte, error) {    var body  []byte    var err error    c := make(chan struct{}, 1)    go func() {        defer close(c) //1. 响应拿到了,并闭c.        var resp *http.Response        resp, err = http.Get(url)        if err != nil {            return        }        defer resp.Body.Close()        body, err = ioutil.ReadAll(resp.Body)//2.同步阻塞调用,直到响应返回。    }()    return func() ([]byte, error) {        <-c //3.因为1处关闭了c,则此处不再同步阻塞等待,即响应已拿到,所以向下执行.        return body, err    }}
func main() {    future := RequestFutureFunction("http://strava.com")        // do many other things, maybe create other futures    body, err := future()    log.Printf("response length: %d", len(body))    log.Printf("request error: %v", err)}
以上代码主要改进:调用者可以拿到错误信息,再者以上代码返回的不再是channel而是一个function, 可以多次调用,即多次读取响应

而第一个版本,只能从channel中读取一次响应信息。以上代码可以工作得不错, 但是用起来麻烦且不通用, 所以要抽象一下。

见go代码:


func Future(f func() (interface{}, error)) func() (interface{}, error) {    var result interface{}    var err error    c := make(chan struct{}, 1)    go func() {        defer close(c)        result, err = f()    }()    return func() (interface{}, error) {        <-c        return result, err    }}
func main() {    url := "http://labs.strava.com"    future := Future(func() (interface{}, error) {        resp, err := http.Get(url)        if err != nil {            return nil, err        }        defer resp.Body.Close()        return ioutil.ReadAll(resp.Body)    })    // do many other things    b, err := future()    body, _ := b.([]byte)    log.Printf("response length: %d", len(body))    log.Printf("request error: %v", err)}
以上代码为抽象封装后的代码, 使用时, 需调用者传入同步执行的函数,而以上代码对这个同步函数进行异步封装,成为Futures/Promises.

其实代码改进到此处,已经非常明晰好用,只是因为golang不支持C++那样的模板机制, 所以存在:[]byte -> interface{} -> []byte.

这样的数据类型转换,所以在使用时, 如果数据类型不匹配, 则类型转换很可能失败引发运行时异常, 所以此篇

文章作者给出一个方案:代码生成器,实现代码我没看, 估计是不同的类型实现,用id表记,再提供一个工厂方法,

这样就可以以id获取对应数据类型的Futures/Promises代码实现,具体如何,您有时间可以深入研究一下!


注意: 此文章只是我个人笔记, 如有错漏,请一定指正, 共同学习, 我的邮箱: htyu_0203_39@sina.com


1 0
原创粉丝点击