其它语言中Future和Promise的概念大量存在, 比如Node.js、Scala、Java、C#、C++ 11、Scheme、Swift等,可以方便的实现异步执行和回调。但是在Go语言的世界里,我们是通过goroutine/channel实现这种类似的功能呢,goroutine之间可以通过channel进行通讯, 但是,如果我们还是想使用Future/Promise的功能的话,该如何实现呢?
Future,Promise或Delay是用于并发编程的一种设计模式。它们表示一个对象,这个对象用来作为一次计算结果的代理,而该结果开始的时候是未知的,因为计算还没有完成。Promise与Future的区别在于,Future是Promise的一个只读的视图,也就是说Future没有设置任务结果的方法,只能获取任务执行结果或者为Future添加回调函数。
下面演示了实现一个Go Future的实现。因为Go目前还没有泛型的概念,所以为了使代码更通用,我会使用interface{}
代表一个通用的对象。
首先定义一个Future
结构,
go
type Future struct {
result interface{} //计算结果
err error //错误
signal chan struct{} //等待完成的信号
IsCompleted bool //计算是否完成
}
这个Future可以执行下面的计算func() (interface{}, error)
,所以计算方法应该实现为FutureFunc
类型的函数,它会返回结果或者返回error:go
type FutureFunc func() (interface{}, error)
然后定义获取结果的Get
和GetOrTimeout
方法,它会阻塞直到获得结果或者超时(GetOrTimeout)。
`go
// GetOrTimeout is similar to Get(), but GetOrTimeout will not block after timeout.
func (f *Future) GetOrTimeout(d time.Duration) (result interface{}, err error, timeout bool) {
select {
case <-time.After(d):
return nil, nil, true
case <-f.signal:
return f.result, f.err, false
}
}
// Get is used to get future result
func (f *Future) Get() (result interface{}, err error) {
<-f.signal
return f.result, f.err
}
`
然后定义NewFuture
就可以了:
`go
func NewFuture(fun FutureFunc) *Future {
f := new(Future)
f.signal = make(chan struct{}, 1)
go func() {
defer close(f.signal)
result, err := fun()
f.result = result
f.err = err
f.IsCompleted = true
}()
return f
}
`
一个使用的例子:
`go
func ExampleRequestFuture() {
requestFunc := func() (body interface{}, err error) {
url := "http://www.baidu.com"
var resp *http.Response
resp, err = http.Get(url)
if err != nil {
return
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
return string(bodyBytes), err
}
requestFuture := NewFuture(FutureFunc(requestFunc))
body, err, timeout := requestFuture.GetOrTimeout(10 * time.Second)
if timeout {
fmt.Println("timeout")
} else {
if err != nil {
fmt.Printf("error: %v\n", err)
} else {
fmt.Printf("body: %v\n", body)
}
}
}
`
如果你是一个Java程序了,可以发现这个Future类似Java中的Future接口。
当然这个Future实现的还是非常的简陋,至少还应该实现回调接口比如OnSuccess
、OnFailure
、OnComplete
等方法,另外一些方法如Cancel
也应该加上。
为了组合多个Future,避免掉入"回调陷阱",还应该实现Future的组合方法。
为了实现SetResult
和SetError
的功能,可以实现一个类似的Promise
的功能。
但是,目前我不会去实现这个功能,一是目前我没有这方面的需求,而是 @fanliao已经实现了这样的一个框架,名字叫go-promise,代码放在了github上,我们不必再重复造轮子了。
这个框架提供了丰富的功能:
Future and Promise
NewPromise()
promise.Future
Promise and Future callbacks
.OnSuccess(v interface{})
.OnFailure(v interface{})
.OnComplete(v interface{})
.OnCancel()
Get the result of future
.Get()
.GetOrTimeout()
.GetChan()
Set timeout for future
.SetTimeout(ms)
Merge multiple promises
WhenAll(func1, func2, func3, ...)
WhenAny(func1, func2, func3, ...)
WhenAnyMatched(func1, func2, func3, ...)
Pipe
.Pipe(funcWithDone, funcWithFail)
Cancel the future
.Cancel()
.IsCancelled()
Create future by function
Start(func() (r interface{}, e error))
Start(func())
Start(func(canceller Canceller) (r interface{}, e error))
Start(func(canceller Canceller))
Immediate wrappers
Wrap(interface{})
Chain API
Start(taskDone).Done(done1).Fail(fail1).Always(alwaysForDone1).Pipe(f1, f2).Done(done2)
使用例子可以看他的项目文档。
参考资料
- https://en.wikipedia.org/wiki/Futures_and_promises
- http://labs.strava.com/blog/futures-in-golang/
- https://github.com/fanliao/go-promise
有疑问加站长微信联系(非本文作者)