使用Golang實現Futures 和 Promises
其它語言中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
來源:http://colobu.com/2016/04/14/Futures-and-Promises-in-golang/