go-promise is a Go promise and future library.
Inspired by Futures and promises
$ go get github.com/fanliao/go-promise
-
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)
import "github.com/fanliao/go-promise"
import "net/http"
p := promise.NewPromise()
p.OnSuccess(func(v interface{}) {
...
}).OnFailure(func(v interface{}) {
...
}).OnComplete(func(v interface{}) {
...
})
go func(){
url := "http://example.com/"
resp, err := http.Get(url)
defer resp.Body.Close()
if err != nil {
p.Reject(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
p.Reject(err)
}
p.Resolve(body)
}()
r, err := p.Get()
If you want to provide a read-only view, you can get a future variable:
p.Future //cannot Resolve, Reject for a future
Can use Start function to submit a future task, it will return a future variable, so cannot Resolve or Reject the future outside of Start function:
import "github.com/fanliao/go-promise"
import "net/http"
task := func()(r interface{}, err error){
url := "http://example.com/"
resp, err := http.Get(url)
defer resp.Body.Close()
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
f := promise.Start(task).OnSuccess(func(v interface{}) {
...
}).OnFailure(func(v interface{}) {
...
}).OnComplete(func(v interface{}) {
...
})
r, err := f.Get()
Please note the process will be block until the future task is completed
f := promise.Start(func() (r interface{}, err error) {
return "ok", nil
})
r, err := f.Get() //return "ok", nil
f := promise.Start(func() (r interface{}, err error) {
return nil, errors.New("fail")
})
r, err := f.Get() //return nil, errorString{"fail"}
Can wait until timeout
f := promise.Start(func() (r interface{}, err error) {
time.Sleep(500 * time.Millisecond)
return "ok", nil
})
r, err, timeout := f.GetOrTimeout(100) //return nil, nil, true
Creates a future that will be completed when all of the supplied future are completed.
task1 := func() (r interface{}, err error) {
return "ok1", nil
}
task2 := func() (r interface{}, err error) {
return "ok2", nil
}
f := promise.WhenAll(task1, task2)
r, err := f.Get() //return []interface{}{"ok1", "ok2"}
If any future is failure, the future returnd by WhenAll will be failure
task1 := func() (r interface{}, err error) {
return "ok", nil
}
task2 := func() (r interface{}, err error) {
return nil, errors.New("fail2")
}
f := promise.WhenAll(task1, task2)
r, ok := f.Get() //return nil, *AggregateError
Creates a future that will be completed when any of the supplied tasks is completed.
task1 := func() (r interface{}, err error) {
return "ok1", nil
}
task2 := func() (r interface{}, err error) {
time.Sleep(200 * time.Millisecond)
return nil, errors.New("fail2")
}
f := promise.WhenAny(task1, task2)
r, err := f.Get() //return "ok1", nil
Also can add a predicate function by WhenAnyMatched, the future that will be completed when any of the supplied tasks is completed and match the predicate.
task1 := func() (r interface{}, err error) {
time.Sleep(200 * time.Millisecond)
return "ok1", nil
}
task2 := func() (r interface{}, err error) {
return "ok2", nil
}
f := promise.WhenAnyMatched(func(v interface{}) bool{
return v == "ok1"
}, task1, task2)
r, err := f.Get() //return "ok1", nil
task1 := func() (r interface{}, err error) {
return 10, nil
}
task2 := func(v interface{}) (r interface{}, err error) {
return v.(int) * 2, nil
}
f := promise.Start(task1).Pipe(task2)
r, err := f.Get() //return 20
If need cancel a future, can pass a canceller object to task function
import "github.com/fanliao/go-promise"
import "net/http"
p := promise.NewPromise().EnableCanceller()
go func(canceller promise.Canceller){
for i < 50 {
if canceller.IsCancelled() {
return
}
time.Sleep(100 * time.Millisecond)
}
}(p.Canceller())
f.Cancel()
r, err := p.Get() //return nil, promise.CANCELLED
fmt.Println(p.Future.IsCancelled()) //true
Or can use Start to submit a future task which can be cancelled
task := func(canceller promise.Canceller) (r interface{}, err error) {
for i < 50 {
if canceller.IsCancelled() {
return 0, nil
}
time.Sleep(100 * time.Millisecond)
}
return 1, nil
}
f := promise.Start(task1)
f.Cancel()
r, err := f.Get() //return nil, promise.CANCELLED
fmt.Println(f.IsCancelled()) //true
When call WhenAny() function, if a future is completed correctly, then will try to check if other futures enable cancel. If yes, will request cancelling all other futures.
You can also set timeout for a future
task := func(canceller promise.Canceller) (r interface{}, err error) {
time.Sleep(300 * time.Millisecond)
if !canceller.IsCancelled(){
fmt.Println("Run done")
}
return
}
f := promise.Start(task).OnCancel(func() {
fmt.Println("Future is cancelled")
}).SetTimeout(100)
r, err := f.Get() //return nil, promise.CANCELLED
fmt.Println(f.IsCancelled()) //print true
go-promise is licensed under the MIT Licence, (http://www.apache.org/licenses/LICENSE-2.0.html).