Repository: fanliao/go-promise Branch: master Commit: 1890db352a72 Files: 8 Total size: 58.1 KB Directory structure: gitextract__0ann36z/ ├── .gitignore ├── LICENSE ├── README.md ├── future.go ├── future_factory.go ├── future_test.go ├── promise.go └── utils.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 fanliao Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [home]: github.com/fanliao/go-promise go-promise is a Go promise and future library. Inspired by [Futures and promises]() ## Installation $ go get github.com/fanliao/go-promise ## Features * 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)``` ## Quick start ### Promise and Future ```go 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: ```go 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: ```go 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() ``` ### Get the result of future Please note the process will be block until the future task is completed ```go 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 ```go 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 ``` ### Merge multiple futures Creates a future that will be completed when all of the supplied future are completed. ```go 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 ```go 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. ```go 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. ```go 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 ``` ### Promise pipelining ```go 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 ``` ### Cancel the future or set timeout If need cancel a future, can pass a canceller object to task function ```go 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 ```go 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 ```go 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 ``` ## Document * [GoDoc at godoc.org](http://godoc.org/github.com/fanliao/go-promise) ## License go-promise is licensed under the MIT Licence, (http://www.apache.org/licenses/LICENSE-2.0.html). ================================================ FILE: future.go ================================================ /* Package promise provides a complete promise and future implementation. A quick start sample: fu := Start(func()(resp interface{}, err error){ resp, err = http.Get("http://example.com/") return }) //do somthing... resp, err := fu.Get() */ package promise import ( "errors" "fmt" "sync/atomic" "time" "unsafe" ) type callbackType int const ( CALLBACK_DONE callbackType = iota CALLBACK_FAIL CALLBACK_ALWAYS CALLBACK_CANCEL ) //pipe presents a promise that will be chain call type pipe struct { pipeDoneTask, pipeFailTask func(v interface{}) *Future pipePromise *Promise } //getPipe returns piped Future task function and pipe Promise by the status of current Promise. func (this *pipe) getPipe(isResolved bool) (func(v interface{}) *Future, *Promise) { if isResolved { return this.pipeDoneTask, this.pipePromise } else { return this.pipeFailTask, this.pipePromise } } //Canceller is used to check if the future is cancelled //It usually be passed to the future task function //for future task function can check if the future is cancelled. type Canceller interface { IsCancelled() bool Cancel() } //canceller provides an implement of Canceller interface. //It will be passed to future task function as paramter type canceller struct { f *Future } //Cancel sets Future task to CANCELLED status func (this *canceller) Cancel() { this.f.Cancel() } //IsCancelled returns true if Future task is cancelld, otherwise false. func (this *canceller) IsCancelled() (r bool) { return this.f.IsCancelled() } //futureVal stores the internal state of Future. type futureVal struct { dones, fails, always []func(v interface{}) cancels []func() pipes []*pipe r *PromiseResult } //Future provides a read-only view of promise, //the value is set by using Resolve, Reject and Cancel methods of related Promise type Future struct { Id int //Id can be used as identity of Future final chan struct{} //val point to futureVal that stores status of future //if need to change the status of future, must copy a new futureVal and modify it, //then use CAS to put the pointer of new futureVal val unsafe.Pointer } //Canceller returns a canceller object related to future. func (this *Future) Canceller() Canceller { return &canceller{this} } //IsCancelled returns true if the promise is cancelled, otherwise false func (this *Future) IsCancelled() bool { val := this.loadVal() if val != nil && val.r != nil && val.r.Typ == RESULT_CANCELLED { return true } else { return false } } //SetTimeout sets the future task will be cancelled //if future is not complete before time out func (this *Future) SetTimeout(mm int) *Future { if mm == 0 { mm = 10 } else { mm = mm * 1000 * 1000 } go func() { <-time.After((time.Duration)(mm) * time.Nanosecond) this.Cancel() }() return this } //GetChan returns a channel than can be used to receive result of Promise func (this *Future) GetChan() <-chan *PromiseResult { c := make(chan *PromiseResult, 1) this.OnComplete(func(v interface{}) { c <- this.loadResult() }).OnCancel(func() { c <- this.loadResult() }) return c } //Get will block current goroutines until the Future is resolved/rejected/cancelled. //If Future is resolved, value and nil will be returned //If Future is rejected, nil and error will be returned. //If Future is cancelled, nil and CANCELLED error will be returned. func (this *Future) Get() (val interface{}, err error) { <-this.final return getFutureReturnVal(this.loadResult()) } //GetOrTimeout is similar to Get(), but GetOrTimeout will not block after timeout. //If GetOrTimeout returns with a timeout, timeout value will be true in return values. //The unit of paramter is millisecond. func (this *Future) GetOrTimeout(mm uint) (val interface{}, err error, timout bool) { if mm == 0 { mm = 10 } else { mm = mm * 1000 * 1000 } select { case <-time.After((time.Duration)(mm) * time.Nanosecond): return nil, nil, true case <-this.final: r, err := getFutureReturnVal(this.loadResult()) return r, err, false } } //Cancel sets the status of promise to RESULT_CANCELLED. //If promise is cancelled, Get() will return nil and CANCELLED error. //All callback functions will be not called if Promise is cancalled. func (this *Future) Cancel() (e error) { return this.setResult(&PromiseResult{CANCELLED, RESULT_CANCELLED}) } //OnSuccess registers a callback function that will be called when Promise is resolved. //If promise is already resolved, the callback will immediately called. //The value of Promise will be paramter of Done callback function. func (this *Future) OnSuccess(callback func(v interface{})) *Future { this.addCallback(callback, CALLBACK_DONE) return this } //OnFailure registers a callback function that will be called when Promise is rejected. //If promise is already rejected, the callback will immediately called. //The error of Promise will be paramter of Fail callback function. func (this *Future) OnFailure(callback func(v interface{})) *Future { this.addCallback(callback, CALLBACK_FAIL) return this } //OnComplete register a callback function that will be called when Promise is rejected or resolved. //If promise is already rejected or resolved, the callback will immediately called. //According to the status of Promise, value or error will be paramter of Always callback function. //Value is the paramter if Promise is resolved, or error is the paramter if Promise is rejected. //Always callback will be not called if Promise be called. func (this *Future) OnComplete(callback func(v interface{})) *Future { this.addCallback(callback, CALLBACK_ALWAYS) return this } //OnCancel registers a callback function that will be called when Promise is cancelled. //If promise is already cancelled, the callback will immediately called. func (this *Future) OnCancel(callback func()) *Future { this.addCallback(callback, CALLBACK_CANCEL) return this } //Pipe registers one or two functions that returns a Future, and returns a proxy of pipeline Future. //First function will be called when Future is resolved, the returned Future will be as pipeline Future. //Secondary function will be called when Futrue is rejected, the returned Future will be as pipeline Future. func (this *Future) Pipe(callbacks ...interface{}) (result *Future, ok bool) { if len(callbacks) == 0 || (len(callbacks) == 1 && callbacks[0] == nil) || (len(callbacks) > 1 && callbacks[0] == nil && callbacks[1] == nil) { result = this return } //ensure all callback functions match the spec "func(v interface{}) *Future" cs := make([]func(v interface{}) *Future, len(callbacks), len(callbacks)) for i, callback := range callbacks { if c, ok1 := callback.(func(v interface{}) *Future); ok1 { cs[i] = c } else if c, ok1 := callback.(func() *Future); ok1 { cs[i] = func(v interface{}) *Future { return c() } } else if c, ok1 := callback.(func(v interface{})); ok1 { cs[i] = func(v interface{}) *Future { return Start(func() { c(v) }) } } else if c, ok1 := callback.(func(v interface{}) (r interface{}, err error)); ok1 { cs[i] = func(v interface{}) *Future { return Start(func() (r interface{}, err error) { r, err = c(v) return }) } } else if c, ok1 := callback.(func()); ok1 { cs[i] = func(v interface{}) *Future { return Start(func() { c() }) } } else if c, ok1 := callback.(func() (r interface{}, err error)); ok1 { cs[i] = func(v interface{}) *Future { return Start(func() (r interface{}, err error) { r, err = c() return }) } } else { ok = false return } } for { v := this.loadVal() r := v.r if r != nil { result = this if r.Typ == RESULT_SUCCESS && cs[0] != nil { result = (cs[0](r.Result)) } else if r.Typ == RESULT_FAILURE && len(cs) > 1 && cs[1] != nil { result = (cs[1](r.Result)) } } else { newPipe := &pipe{} newPipe.pipeDoneTask = cs[0] if len(cs) > 1 { newPipe.pipeFailTask = cs[1] } newPipe.pipePromise = NewPromise() newVal := *v newVal.pipes = append(newVal.pipes, newPipe) //use CAS to ensure that the state of Future is not changed, //if the state is changed, will retry CAS operation. if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) { result = newPipe.pipePromise.Future break } } } ok = true return } //result uses Atomic load to return result of the Future func (this *Future) loadResult() *PromiseResult { val := this.loadVal() return val.r } //val uses Atomic load to return state value of the Future func (this *Future) loadVal() *futureVal { r := atomic.LoadPointer(&this.val) return (*futureVal)(r) } //setResult sets the value and final status of Promise, it will only be executed for once func (this *Future) setResult(r *PromiseResult) (e error) { //r *PromiseResult) { defer func() { if err := getError(recover()); err != nil { e = err fmt.Println("\nerror in setResult():", err) } }() e = errors.New("Cannot resolve/reject/cancel more than once") for { v := this.loadVal() if v.r != nil { return } newVal := *v newVal.r = r //Use CAS operation to ensure that the state of Promise isn't changed. //If the state is changed, must get latest state and try to call CAS again. //No ABA issue in this case because address of all objects are different. if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) { //Close chEnd then all Get() and GetOrTimeout() will be unblocked close(this.final) //call callback functions and start the Promise pipeline if len(v.dones) > 0 || len(v.fails) > 0 || len(v.always) > 0 || len(v.cancels) > 0 { go func() { execCallback(r, v.dones, v.fails, v.always, v.cancels) }() } //start the pipeline if len(v.pipes) > 0 { go func() { for _, pipe := range v.pipes { pipeTask, pipePromise := pipe.getPipe(r.Typ == RESULT_SUCCESS) startPipe(r, pipeTask, pipePromise) } }() } e = nil break } } return } //handleOneCallback registers a callback function func (this *Future) addCallback(callback interface{}, t callbackType) { if callback == nil { return } if (t == CALLBACK_DONE) || (t == CALLBACK_FAIL) || (t == CALLBACK_ALWAYS) { if _, ok := callback.(func(v interface{})); !ok { panic(errors.New("Callback function spec must be func(v interface{})")) } } else if t == CALLBACK_CANCEL { if _, ok := callback.(func()); !ok { panic(errors.New("Callback function spec must be func()")) } } for { v := this.loadVal() r := v.r if r == nil { newVal := *v switch t { case CALLBACK_DONE: newVal.dones = append(newVal.dones, callback.(func(v interface{}))) case CALLBACK_FAIL: newVal.fails = append(newVal.fails, callback.(func(v interface{}))) case CALLBACK_ALWAYS: newVal.always = append(newVal.always, callback.(func(v interface{}))) case CALLBACK_CANCEL: newVal.cancels = append(newVal.cancels, callback.(func())) } //use CAS to ensure that the state of Future is not changed, //if the state is changed, will retry CAS operation. if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) { break } } else { if (t == CALLBACK_DONE && r.Typ == RESULT_SUCCESS) || (t == CALLBACK_FAIL && r.Typ == RESULT_FAILURE) || (t == CALLBACK_ALWAYS && r.Typ != RESULT_CANCELLED) { callbackFunc := callback.(func(v interface{})) callbackFunc(r.Result) } else if t == CALLBACK_CANCEL && r.Typ == RESULT_CANCELLED { callbackFunc := callback.(func()) callbackFunc() } break } } } ================================================ FILE: future_factory.go ================================================ package promise import ( "sync/atomic" ) type anyPromiseResult struct { result interface{} i int } //Start start a goroutines to execute task function //and return a Future that presents the result. //If option paramter is true, the act function will be sync called. //Type of act can be any of below four types: // func() (r interface{}, err error): // if err returned by act != nil or panic error, then Future will be rejected with error, // otherwise be resolved with r. // func(): // if act panic error, then Future will be rejected, otherwise be resolved with nil. // func(c promise.Canceller) (r interface{}, err error): // if err returned by act != nil or panic error, // then Future will be rejected with err, otherwise be resolved with r. // We can check c.IsCancelled() to decide whether need to exit act function // func(promise.Canceller): // if act panic error, then Future will be rejected with error, otherwise be resolved with nil. // We can check c.IsCancelled() to decide whether need to exit act function. // error: // Future will be rejected with error immediately // other value: // Future will be resolved with value immediately func Start(act interface{}, syncs ...bool) *Future { pr := NewPromise() if f, ok := act.(*Future); ok { return f } if action := getAct(pr, act); action != nil { if syncs != nil && len(syncs) > 0 && !syncs[0] { //sync call r, err := action() if pr.IsCancelled() { pr.Cancel() } else { if err == nil { pr.Resolve(r) } else { pr.Reject(err) } } } else { //async call go func() { r, err := action() if pr.IsCancelled() { pr.Cancel() } else { if err == nil { pr.Resolve(r) } else { pr.Reject(err) } } }() } } return pr.Future } //Wrap return a Future that presents the wrapped value func Wrap(value interface{}) *Future { pr := NewPromise() if e, ok := value.(error); !ok { pr.Resolve(value) } else { pr.Reject(e) } return pr.Future } //WhenAny returns a Future. //If any Future is resolved, this Future will be resolved and return result of resolved Future. //Otherwise will rejected with results slice returned by all Futures //Legit types of act are same with Start function func WhenAny(acts ...interface{}) *Future { return WhenAnyMatched(nil, acts...) } //WhenAnyMatched returns a Future. //If any Future is resolved and match the predicate, this Future will be resolved and return result of resolved Future. //If all Futures are cancelled, this Future will be cancelled. //Otherwise will rejected with a NoMatchedError included results slice returned by all Futures //Legit types of act are same with Start function func WhenAnyMatched(predicate func(interface{}) bool, acts ...interface{}) *Future { if predicate == nil { predicate = func(v interface{}) bool { return true } } fs := make([]*Future, len(acts)) for i, act := range acts { fs[i] = Start(act) } nf, rs := NewPromise(), make([]interface{}, len(fs)) if len(acts) == 0 { nf.Resolve(nil) } chFails, chDones := make(chan anyPromiseResult), make(chan anyPromiseResult) go func() { for i, f := range fs { k := i f.OnSuccess(func(v interface{}) { defer func() { _ = recover() }() chDones <- anyPromiseResult{v, k} }).OnFailure(func(v interface{}) { defer func() { _ = recover() }() chFails <- anyPromiseResult{v, k} }).OnCancel(func() { defer func() { _ = recover() }() chFails <- anyPromiseResult{CANCELLED, k} }) } }() if len(fs) == 1 { select { case r := <-chFails: if _, ok := r.result.(CancelledError); ok { nf.Cancel() } else { nf.Reject(newNoMatchedError1(r.result)) } case r := <-chDones: if predicate(r.result) { nf.Resolve(r.result) } else { nf.Reject(newNoMatchedError1(r.result)) } } } else { go func() { defer func() { if e := recover(); e != nil { nf.Reject(newErrorWithStacks(e)) } }() j := 0 for { select { case r := <-chFails: rs[r.i] = getError(r.result) case r := <-chDones: if predicate(r.result) { //try to cancel other futures for _, f := range fs { f.Cancel() } //close the channel for avoid the send side be blocked closeChan := func(c chan anyPromiseResult) { defer func() { _ = recover() }() close(c) } closeChan(chDones) closeChan(chFails) //Resolve the future and return result nf.Resolve(r.result) return } else { rs[r.i] = r.result } } if j++; j == len(fs) { m := 0 for _, r := range rs { switch val := r.(type) { case CancelledError: default: m++ _ = val } } if m > 0 { nf.Reject(newNoMatchedError(rs)) } else { nf.Cancel() } break } } }() } return nf.Future } //WhenAll receives function slice and returns a Future. //If all Futures are resolved, this Future will be resolved and return results slice. //Otherwise will rejected with results slice returned by all Futures //Legit types of act are same with Start function func WhenAll(acts ...interface{}) (fu *Future) { pr := NewPromise() fu = pr.Future if len(acts) == 0 { pr.Resolve([]interface{}{}) return } fs := make([]*Future, len(acts)) for i, act := range acts { fs[i] = Start(act) } fu = whenAllFuture(fs...) return } //WhenAll receives Futures slice and returns a Future. //If all Futures are resolved, this Future will be resolved and return results slice. //If any Future is cancelled, this Future will be cancelled. //Otherwise will rejected with results slice returned by all Futures. //Legit types of act are same with Start function func whenAllFuture(fs ...*Future) *Future { wf := NewPromise() rs := make([]interface{}, len(fs)) if len(fs) == 0 { wf.Resolve([]interface{}{}) } else { n := int32(len(fs)) cancelOthers := func(j int) { for k, f1 := range fs { if k != j { f1.Cancel() } } } go func() { isCancelled := int32(0) for i, f := range fs { j := i f.OnSuccess(func(v interface{}) { rs[j] = v if atomic.AddInt32(&n, -1) == 0 { wf.Resolve(rs) } }).OnFailure(func(v interface{}) { if atomic.CompareAndSwapInt32(&isCancelled, 0, 1) { //try to cancel all futures cancelOthers(j) //errs := make([]error, 0, 1) //errs = append(errs, v.(error)) e := newAggregateError1("Error appears in WhenAll:", v) wf.Reject(e) } }).OnCancel(func() { if atomic.CompareAndSwapInt32(&isCancelled, 0, 1) { //try to cancel all futures cancelOthers(j) wf.Cancel() } }) } }() } return wf.Future } ================================================ FILE: future_test.go ================================================ package promise import ( "errors" "fmt" c "github.com/smartystreets/goconvey/convey" "reflect" "strconv" "testing" "time" ) const ( TASK_END = "task be end," CALL_DONE = "callback done," CALL_FAIL = "callback fail," CALL_ALWAYS = "callback always," WAIT_TASK = "wait task end," GET = "get task result," DONE_Pipe_END = "task Pipe done be end," FAIL_Pipe_END = "task Pipe fail be end," ) // errorLinq is a trivial implementation of error. type myError struct { val interface{} } func (e *myError) Error() string { return fmt.Sprintf("%v", e.val) } func newMyError(v interface{}) *myError { return &myError{v} } func TestResolveAndReject(t *testing.T) { c.Convey("When Promise is resolved", t, func() { p := NewPromise() go func() { time.Sleep(50 * time.Millisecond) p.Resolve("ok") }() c.Convey("Should return the argument of Resolve", func() { r, err := p.Get() c.So(r, c.ShouldEqual, "ok") c.So(err, c.ShouldBeNil) }) }) c.Convey("When Promise is rejected", t, func() { p := NewPromise() go func() { time.Sleep(50 * time.Millisecond) p.Reject(errors.New("fail")) }() c.Convey("Should return error", func() { r, err := p.Get() c.So(err, c.ShouldNotBeNil) c.So(r, c.ShouldEqual, nil) }) }) } func TestCancel(t *testing.T) { c.Convey("When Promise is cancelled", t, func() { p := NewPromise() go func() { time.Sleep(50 * time.Millisecond) p.Cancel() }() c.Convey("Should return CancelledError", func() { r, err := p.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldEqual, CANCELLED) c.So(p.IsCancelled(), c.ShouldBeTrue) }) }) } func TestGetOrTimeout(t *testing.T) { timout := 50 * time.Millisecond c.Convey("When Promise is unfinished", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Resolve("ok") }() c.Convey("timeout should be true", func() { r, err, timeout := p.GetOrTimeout(10) c.So(timeout, c.ShouldBeTrue) c.So(r, c.ShouldBeNil) c.So(err, c.ShouldBeNil) }) c.Convey("When Promise is resolved, the argument of Resolve should be returned", func() { r, err, timeout := p.GetOrTimeout(50) c.So(timeout, c.ShouldBeFalse) c.So(r, c.ShouldEqual, "ok") c.So(err, c.ShouldBeNil) }) }) c.Convey("When Promise is rejected", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Reject(errors.New("fail")) }() c.Convey("Should return error", func() { r, err, timeout := p.GetOrTimeout(83) c.So(timeout, c.ShouldBeFalse) c.So(r, c.ShouldBeNil) c.So(err, c.ShouldNotBeNil) }) }) c.Convey("When Promise is cancelled", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Cancel() }() c.Convey("Should return CancelledError", func() { r, err, timeout := p.GetOrTimeout(83) c.So(timeout, c.ShouldBeFalse) c.So(r, c.ShouldBeNil) c.So(err, c.ShouldEqual, CANCELLED) c.So(p.IsCancelled(), c.ShouldBeTrue) }) }) } func TestGetChan(t *testing.T) { timout := 50 * time.Millisecond c.Convey("When Promise is resolved", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Resolve("ok") }() c.Convey("Should receive the argument of Resolve from returned channel", func() { fr, ok := <-p.GetChan() c.So(fr.Result, c.ShouldEqual, "ok") c.So(fr.Typ, c.ShouldEqual, RESULT_SUCCESS) c.So(ok, c.ShouldBeTrue) }) }) c.Convey("When Promise is rejected", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Reject(errors.New("fail")) }() c.Convey("Should receive error from returned channel", func() { fr, ok := <-p.GetChan() c.So(fr.Result, c.ShouldNotBeNil) c.So(fr.Typ, c.ShouldEqual, RESULT_FAILURE) c.So(ok, c.ShouldBeTrue) }) }) c.Convey("When Promise is cancelled", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Cancel() }() c.Convey("Should receive CancelledError from returned channel", func() { fr, ok := <-p.GetChan() c.So(fr.Result, c.ShouldEqual, CANCELLED) c.So(p.IsCancelled(), c.ShouldBeTrue) c.So(fr.Typ, c.ShouldEqual, RESULT_CANCELLED) c.So(ok, c.ShouldBeTrue) }) c.Convey("Should receive CancelledError from returned channel at second time", func() { fr, ok := <-p.GetChan() c.So(fr.Result, c.ShouldEqual, CANCELLED) c.So(p.IsCancelled(), c.ShouldBeTrue) c.So(fr.Typ, c.ShouldEqual, RESULT_CANCELLED) c.So(ok, c.ShouldBeTrue) }) }) } func TestFuture(t *testing.T) { c.Convey("Future can receive return value and status but cannot change the status", t, func() { var fu *Future c.Convey("When Future is resolved", func() { func() { p := NewPromise() go func() { time.Sleep(50 * time.Millisecond) p.Resolve("ok") }() fu = p.Future }() r, err := fu.Get() c.So(r, c.ShouldEqual, "ok") c.So(err, c.ShouldBeNil) }) c.Convey("When Future is rejected", func() { func() { p := NewPromise() go func() { time.Sleep(50 * time.Millisecond) p.Reject(errors.New("fail")) }() fu = p.Future }() r, err := fu.Get() c.So(err, c.ShouldNotBeNil) c.So(r, c.ShouldEqual, nil) }) c.Convey("When Future is cancelled", func() { func() { p := NewPromise() go func() { time.Sleep(50 * time.Millisecond) p.Cancel() }() fu = p.Future }() r, err := fu.Get() c.So(err, c.ShouldNotBeNil) c.So(r, c.ShouldEqual, nil) }) }) } func TestCallbacks(t *testing.T) { timout := 50 * time.Millisecond done, always, fail, cancel := false, false, false, false p := NewPromise() go func() { <-time.After(timout) p.Resolve("ok") }() c.Convey("When Promise is resolved", t, func() { p.OnSuccess(func(v interface{}) { done = true c.Convey("The argument of Done should be 'ok'", t, func() { c.So(v, c.ShouldEqual, "ok") }) }).OnComplete(func(v interface{}) { always = true c.Convey("The argument of Always should be 'ok'", t, func() { c.So(v, c.ShouldEqual, "ok") }) }).OnFailure(func(v interface{}) { fail = true panic("Unexpected calling") }) r, err := p.Get() //The code after Get() and the callback will be concurrent run //So sleep 52 ms to wait all callback be done time.Sleep(52 * time.Millisecond) c.Convey("Should call the Done and Always callbacks", func() { c.So(r, c.ShouldEqual, "ok") c.So(err, c.ShouldBeNil) c.So(done, c.ShouldEqual, true) c.So(always, c.ShouldEqual, true) c.So(fail, c.ShouldEqual, false) }) }) c.Convey("When adding the callback after Promise is resolved", t, func() { done, always, fail := false, false, false p.OnSuccess(func(v interface{}) { done = true c.Convey("The argument of Done should be 'ok'", func() { c.So(v, c.ShouldEqual, "ok") }) }).OnComplete(func(v interface{}) { always = true c.Convey("The argument of Always should be 'ok'", func() { c.So(v, c.ShouldEqual, "ok") }) }).OnFailure(func(v interface{}) { fail = true panic("Unexpected calling") }) c.Convey("Should immediately run the Done and Always callbacks", func() { c.So(done, c.ShouldEqual, true) c.So(always, c.ShouldEqual, true) c.So(fail, c.ShouldEqual, false) }) }) var e *error = nil done, always, fail = false, false, false p = NewPromise() go func() { <-time.After(timout) p.Reject(errors.New("fail")) }() c.Convey("When Promise is rejected", t, func() { p.OnSuccess(func(v interface{}) { done = true panic("Unexpected calling") }).OnComplete(func(v interface{}) { always = true c.Convey("The argument of Always should be error", t, func() { c.So(v, c.ShouldImplement, e) }) }).OnFailure(func(v interface{}) { fail = true c.Convey("The argument of Fail should be error", t, func() { c.So(v, c.ShouldImplement, e) }) }) r, err := p.Get() time.Sleep(52 * time.Millisecond) c.Convey("Should call the Fail and Always callbacks", func() { c.So(r, c.ShouldEqual, nil) c.So(err, c.ShouldNotBeNil) c.So(done, c.ShouldEqual, false) c.So(always, c.ShouldEqual, true) c.So(fail, c.ShouldEqual, true) }) }) c.Convey("When adding the callback after Promise is rejected", t, func() { done, always, fail = false, false, false p.OnSuccess(func(v interface{}) { done = true panic("Unexpected calling") }).OnComplete(func(v interface{}) { always = true c.Convey("The argument of Always should be error", func() { c.So(v, c.ShouldImplement, e) }) }).OnFailure(func(v interface{}) { fail = true c.Convey("The argument of Fail should be error", func() { c.So(v, c.ShouldImplement, e) }) }) c.Convey("Should immediately run the Fail and Always callbacks", func() { c.So(done, c.ShouldEqual, false) c.So(always, c.ShouldEqual, true) c.So(fail, c.ShouldEqual, true) }) }) done, always, fail = false, false, false p = NewPromise() go func() { <-time.After(timout) p.Cancel() }() c.Convey("When Promise is cancelled", t, func() { done, always, fail, cancel = false, false, false, false p.OnSuccess(func(v interface{}) { done = true }).OnComplete(func(v interface{}) { always = true }).OnFailure(func(v interface{}) { fail = true }).OnCancel(func() { cancel = true }) r, err := p.Get() time.Sleep(62 * time.Millisecond) c.Convey("Only cancel callback be called", func() { c.So(r, c.ShouldBeNil) c.So(err, c.ShouldNotBeNil) c.So(done, c.ShouldEqual, false) c.So(always, c.ShouldEqual, false) c.So(fail, c.ShouldEqual, false) c.So(cancel, c.ShouldEqual, true) }) }) c.Convey("When adding the callback after Promise is cancelled", t, func() { done, always, fail, cancel = false, false, false, false p.OnSuccess(func(v interface{}) { done = true }).OnComplete(func(v interface{}) { always = true }).OnFailure(func(v interface{}) { fail = true }).OnCancel(func() { cancel = true }) c.Convey("Should not call any callbacks", func() { c.So(done, c.ShouldEqual, false) c.So(always, c.ShouldEqual, false) c.So(fail, c.ShouldEqual, false) c.So(cancel, c.ShouldEqual, true) }) }) } func TestStart(t *testing.T) { c.Convey("Test start func()", t, func() { c.Convey("When task completed", func() { f := Start(func() {}) r, err := f.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldBeNil) }) c.Convey("When task panic error", func() { f := Start(func() { panic("fail") }) r, err := f.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldNotBeNil) }) }) c.Convey("Test start func()(interface{}, error)", t, func() { c.Convey("When task completed", func() { f := Start(func() (interface{}, error) { time.Sleep(10) return "ok", nil }) r, err := f.Get() c.So(r, c.ShouldEqual, "ok") c.So(err, c.ShouldBeNil) }) c.Convey("When task returned error", func() { f := Start(func() (interface{}, error) { time.Sleep(10) return "fail", errors.New("fail") }) r, err := f.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldNotBeNil) }) c.Convey("When task panic error", func() { f := Start(func() (interface{}, error) { panic("fail") }) r, err := f.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldNotBeNil) }) }) c.Convey("Test start func(canceller Canceller)", t, func() { c.Convey("When task completed", func() { f := Start(func(canceller Canceller) { time.Sleep(10) }) r, err := f.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldBeNil) }) c.Convey("When task be cancelled", func() { f := Start(func(canceller Canceller) { time.Sleep(10) if canceller.IsCancelled() { return } }) f.Cancel() r, err := f.Get() c.So(f.IsCancelled(), c.ShouldBeTrue) c.So(r, c.ShouldBeNil) c.So(err, c.ShouldEqual, CANCELLED) c.So(f.IsCancelled(), c.ShouldBeTrue) }) c.Convey("When task panic error", func() { f := Start(func(canceller Canceller) { panic("fail") }) r, err := f.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldNotBeNil) }) }) c.Convey("Test start func(canceller Canceller)(interface{}, error)", t, func() { c.Convey("When task be cancenlled", func() { task := func(canceller Canceller) (interface{}, error) { i := 0 for i < 50 { if canceller.IsCancelled() { return nil, nil } time.Sleep(100 * time.Millisecond) } panic("exception") } f := Start(task) f.Cancel() r, err := f.Get() c.So(f.IsCancelled(), c.ShouldBeTrue) c.So(r, c.ShouldBeNil) c.So(err, c.ShouldEqual, CANCELLED) c.So(f.IsCancelled(), c.ShouldBeTrue) }) c.Convey("When task panic error", func() { f := Start(func(canceller Canceller) (interface{}, error) { panic("fail") }) r, err := f.Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldNotBeNil) }) }) } func TestPipe(t *testing.T) { timout := 50 * time.Millisecond taskDonePipe := func(v interface{}) *Future { return Start(func() (interface{}, error) { <-time.After(timout) return v.(string) + "2", nil }) } taskFailPipe := func() (interface{}, error) { <-time.After(timout) return "fail2", nil } c.Convey("When task completed", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Resolve("ok") }() fu, ok := p.Pipe(taskDonePipe, taskFailPipe) r, err := fu.Get() c.Convey("the done callback will be called, the future returned by done callback will be returned as chain future", func() { c.So(r, c.ShouldEqual, "ok2") c.So(err, c.ShouldBeNil) c.So(ok, c.ShouldEqual, true) }) }) c.Convey("When task failed", t, func() { p := NewPromise() go func() { <-time.After(timout) p.Reject(errors.New("fail")) }() fu, ok := p.Pipe(taskDonePipe, taskFailPipe) r, err := fu.Get() c.Convey("the fail callback will be called, the future returned by fail callback will be returned as chain future", func() { c.So(r, c.ShouldEqual, "fail2") c.So(err, c.ShouldBeNil) c.So(ok, c.ShouldEqual, true) }) }) c.Convey("Test pipe twice", t, func() { p := NewPromise() pipeFuture1, ok1 := p.Pipe(taskDonePipe, taskFailPipe) c.Convey("Calling Pipe succeed at first time", func() { c.So(ok1, c.ShouldEqual, true) }) pipeFuture2, ok2 := p.Pipe(taskDonePipe, taskFailPipe) c.Convey("Calling Pipe succeed at second time", func() { c.So(ok2, c.ShouldEqual, true) }) p.Resolve("ok") r, _ := pipeFuture1.Get() c.Convey("Pipeline future 1 should return ok2", func() { c.So(r, c.ShouldEqual, "ok2") }) r2, _ := pipeFuture2.Get() c.Convey("Pipeline future 2 should return ok2", func() { c.So(r2, c.ShouldEqual, "ok2") }) }) } func TestWhenAny(t *testing.T) { c.Convey("Test WhenAny", t, func() { whenAnyTasks := func(t1 int, t2 int) *Future { timeouts := []time.Duration{time.Duration(t1), time.Duration(t2)} getTask := func(i int) func() (interface{}, error) { return func() (interface{}, error) { if timeouts[i] > 0 { time.Sleep(timeouts[i] * time.Millisecond) return "ok" + strconv.Itoa(i), nil } else { time.Sleep((-1 * timeouts[i]) * time.Millisecond) return nil, newMyError("fail" + strconv.Itoa(i)) } } } task0 := getTask(0) task1 := getTask(1) f := WhenAny(task0, task1) return f } c.Convey("When all tasks completed, and task 1 be first to complete", func() { r, err := whenAnyTasks(200, 250).Get() c.So(r, c.ShouldEqual, "ok0") c.So(err, c.ShouldBeNil) }) c.Convey("When all tasks completed, and task 2 be first to complete", func() { r, err := whenAnyTasks(280, 250).Get() c.So(r, c.ShouldEqual, "ok1") c.So(err, c.ShouldBeNil) }) c.Convey("When all tasks failed", func() { r, err := whenAnyTasks(-280, -250).Get() errs := err.(*NoMatchedError).Results c.So(r, c.ShouldBeNil) c.So(errs[0].(*myError).val, c.ShouldEqual, "fail0") c.So(errs[1].(*myError).val, c.ShouldEqual, "fail1") }) c.Convey("When one task completed", func() { r, err := whenAnyTasks(-280, 150).Get() c.So(r, c.ShouldEqual, "ok1") c.So(err, c.ShouldBeNil) }) c.Convey("When no task be passed", func() { r, err := WhenAny().Get() c.So(r, c.ShouldBeNil) c.So(err, c.ShouldBeNil) }) }) c.Convey("Test WhenAny, and task can be cancelled", t, func() { var c1, c2 bool whenAnyCanCancelTasks := func(t1 int, t2 int) *Future { timeouts := []time.Duration{time.Duration(t1), time.Duration(t2)} getTask := func(i int) func(canceller Canceller) (interface{}, error) { return func(canceller Canceller) (interface{}, error) { for j := 0; j < 10; j++ { if timeouts[i] > 0 { time.Sleep(timeouts[i] * time.Millisecond) } else { time.Sleep((-1 * timeouts[i]) * time.Millisecond) } if canceller.IsCancelled() { if i == 0 { c1 = true } else { c2 = true } return nil, nil } } if timeouts[i] > 0 { return "ok" + strconv.Itoa(i), nil } else { return nil, newMyError("fail" + strconv.Itoa(i)) } } } task0 := getTask(0) task1 := getTask(1) f := WhenAny(Start(task0), Start(task1)) return f } c.Convey("When task 1 is the first to complete, task 2 will be cancelled", func() { r, err := whenAnyCanCancelTasks(10, 250).Get() c.So(r, c.ShouldEqual, "ok0") c.So(err, c.ShouldBeNil) time.Sleep(1000 * time.Millisecond) c.So(c2, c.ShouldEqual, true) }) c.Convey("When task 2 is the first to complete, task 1 will be cancelled", func() { r, err := whenAnyCanCancelTasks(200, 10).Get() c.So(r, c.ShouldEqual, "ok1") c.So(err, c.ShouldBeNil) time.Sleep(1000 * time.Millisecond) c.So(c1, c.ShouldEqual, true) }) }) } func TestWhenAnyTrue(t *testing.T) { c1, c2 := false, false startTwoCanCancelTask := func(t1 int, t2 int, predicate func(interface{}) bool) *Future { timeouts := []time.Duration{time.Duration(t1), time.Duration(t2)} getTask := func(i int) func(canceller Canceller) (interface{}, error) { return func(canceller Canceller) (interface{}, error) { for j := 0; j < 10; j++ { if timeouts[i] > 0 { time.Sleep(timeouts[i] * time.Millisecond) } else { time.Sleep((-1 * timeouts[i]) * time.Millisecond) } if canceller.IsCancelled() { if i == 0 { c1 = true } else { c2 = true } return nil, nil } } if timeouts[i] > 0 { return "ok" + strconv.Itoa(i), nil } else { return nil, newMyError("fail" + strconv.Itoa(i)) } } } task0 := getTask(0) task1 := getTask(1) f := WhenAnyMatched(predicate, Start(task0), Start(task1)) return f } //第一个任务先完成,第二个后完成,并且设定条件为返回值==第一个的返回值 c.Convey("When the task1 is the first to complete, and predicate returns true", t, func() { r, err := startTwoCanCancelTask(30, 250, func(v interface{}) bool { return v.(string) == "ok0" }).Get() c.So(r, c.ShouldEqual, "ok0") c.So(err, c.ShouldBeNil) time.Sleep(1000 * time.Millisecond) c.So(c2, c.ShouldEqual, true) }) //第一个任务后完成,第二个先完成,并且设定条件为返回值==第二个的返回值 c.Convey("When the task2 is the first to complete, and predicate returns true", t, func() { c1, c2 = false, false r, err := startTwoCanCancelTask(230, 50, func(v interface{}) bool { return v.(string) == "ok1" }).Get() c.So(r, c.ShouldEqual, "ok1") c.So(err, c.ShouldBeNil) time.Sleep(1000 * time.Millisecond) c.So(c1, c.ShouldEqual, true) }) //第一个任务后完成,第二个先完成,并且设定条件为返回值不等于任意一个任务的返回值 c.Convey("When the task2 is the first to complete, and predicate always returns false", t, func() { c1, c2 = false, false r, err := startTwoCanCancelTask(30, 250, func(v interface{}) bool { return v.(string) == "ok11" }).Get() _, ok := err.(*NoMatchedError) c.So(r, c.ShouldBeNil) c.So(ok, c.ShouldBeTrue) c.So(err, c.ShouldNotBeNil) time.Sleep(1000 * time.Millisecond) c.So(c1, c.ShouldEqual, false) c.So(c2, c.ShouldEqual, false) }) //c.Convey("When all tasks be cancelled", t, func() { // getTask := func(canceller Canceller) (interface{}, error) { // for { // time.Sleep(50 * time.Millisecond) // if canceller.IsCancellationRequested() { // canceller.Cancel() // return nil, nil // } // } // } // f1 := Start(getTask) // f2 := Start(getTask) // f3 := WhenAnyMatched(nil, f1, f2) // f1.RequestCancel() // f2.RequestCancel() // r, _ := f3.Get() // c.So(r, c.ShouldBeNil) //}) } func TestWhenAll(t *testing.T) { startTwoTask := func(t1 int, t2 int) (f *Future) { timeouts := []time.Duration{time.Duration(t1), time.Duration(t2)} getTask := func(i int) func() (interface{}, error) { return func() (interface{}, error) { if timeouts[i] > 0 { time.Sleep(timeouts[i] * time.Millisecond) return "ok" + strconv.Itoa(i), nil } else { time.Sleep((-1 * timeouts[i]) * time.Millisecond) return nil, newMyError("fail" + strconv.Itoa(i)) } } } task0 := getTask(0) task1 := getTask(1) f = WhenAll(task0, task1) return f } c.Convey("Test WhenAllFuture", t, func() { whenTwoTask := func(t1 int, t2 int) *Future { return startTwoTask(t1, t2) } c.Convey("When all tasks completed, and the task1 is the first to complete", func() { r, err := whenTwoTask(200, 230).Get() c.So(r, shouldSlicesReSame, []interface{}{"ok0", "ok1"}) c.So(err, c.ShouldBeNil) }) c.Convey("When all tasks completed, and the task1 is the first to complete", func() { r, err := whenTwoTask(230, 200).Get() c.So(r, shouldSlicesReSame, []interface{}{"ok0", "ok1"}) c.So(err, c.ShouldBeNil) }) c.Convey("When task1 failed, but task2 is completed", func() { r, err := whenTwoTask(-250, 210).Get() c.So(err.(*AggregateError).InnerErrs[0].(*myError).val, c.ShouldEqual, "fail0") c.So(r, c.ShouldBeNil) }) c.Convey("When all tasks failed", func() { r, err := whenTwoTask(-250, -110).Get() c.So(err.(*AggregateError).InnerErrs[0].(*myError).val, c.ShouldEqual, "fail1") c.So(r, c.ShouldBeNil) }) c.Convey("When no task be passed", func() { r, err := whenAllFuture().Get() c.So(r, shouldSlicesReSame, []interface{}{}) c.So(err, c.ShouldBeNil) }) c.Convey("When all tasks be cancelled", func() { getTask := func(canceller Canceller) (interface{}, error) { for { time.Sleep(50 * time.Millisecond) if canceller.IsCancelled() { return nil, nil } } } f1 := Start(getTask) f2 := Start(getTask) f3 := WhenAll(f1, f2) f1.Cancel() f2.Cancel() r, _ := f3.Get() c.So(r, c.ShouldBeNil) }) }) } func TestWrap(t *testing.T) { c.Convey("Test Wrap a value", t, func() { r, err := Wrap(10).Get() c.So(r, c.ShouldEqual, 10) c.So(err, c.ShouldBeNil) }) } func shouldSlicesReSame(actual interface{}, expected ...interface{}) string { actualSlice, expectedSlice := reflect.ValueOf(actual), reflect.ValueOf(expected[0]) if actualSlice.Kind() != expectedSlice.Kind() { return fmt.Sprintf("Expected1: '%v'\nActual: '%v'\n", expected[0], actual) } if actualSlice.Kind() != reflect.Slice { return fmt.Sprintf("Expected2: '%v'\nActual: '%v'\n", expected[0], actual) } if actualSlice.Len() != expectedSlice.Len() { return fmt.Sprintf("Expected3: '%v'\nActual: '%v'\n", expected[0], actual) } for i := 0; i < actualSlice.Len(); i++ { if !reflect.DeepEqual(actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface()) { return fmt.Sprintf("Expected4: '%v'\nActual: '%v'\n", expected[0], actual) } } return "" } ================================================ FILE: promise.go ================================================ package promise import ( "math/rand" "unsafe" ) var ( CANCELLED error = &CancelledError{} ) //CancelledError present the Future object is cancelled. type CancelledError struct { } func (e *CancelledError) Error() string { return "Task be cancelled" } //resultType present the type of Future final status. type resultType int const ( RESULT_SUCCESS resultType = iota RESULT_FAILURE RESULT_CANCELLED ) //PromiseResult presents the result of a promise. //If Typ is RESULT_SUCCESS, Result field will present the returned value of Future task. //If Typ is RESULT_FAILURE, Result field will present a related error . //If Typ is RESULT_CANCELLED, Result field will be null. type PromiseResult struct { Result interface{} //result of the Promise Typ resultType //success, failure, or cancelled? } //Promise presents an object that acts as a proxy for a result. //that is initially unknown, usually because the computation of its //value is yet incomplete (refer to wikipedia). //You can use Resolve/Reject/Cancel to set the final result of Promise. //Future can return a read-only placeholder view of result. type Promise struct { *Future } //Cancel sets the status of promise to RESULT_CANCELLED. //If promise is cancelled, Get() will return nil and CANCELLED error. //All callback functions will be not called if Promise is cancalled. func (this *Promise) Cancel() (e error) { return this.Future.Cancel() } //Resolve sets the value for promise, and the status will be changed to RESULT_SUCCESS. //if promise is resolved, Get() will return the value and nil error. func (this *Promise) Resolve(v interface{}) (e error) { return this.setResult(&PromiseResult{v, RESULT_SUCCESS}) } //Resolve sets the error for promise, and the status will be changed to RESULT_FAILURE. //if promise is rejected, Get() will return nil and the related error value. func (this *Promise) Reject(err error) (e error) { return this.setResult(&PromiseResult{err, RESULT_FAILURE}) } //OnSuccess registers a callback function that will be called when Promise is resolved. //If promise is already resolved, the callback will immediately called. //The value of Promise will be paramter of Done callback function. func (this *Promise) OnSuccess(callback func(v interface{})) *Promise { this.Future.OnSuccess(callback) return this } //OnFailure registers a callback function that will be called when Promise is rejected. //If promise is already rejected, the callback will immediately called. //The error of Promise will be paramter of Fail callback function. func (this *Promise) OnFailure(callback func(v interface{})) *Promise { this.Future.OnFailure(callback) return this } //OnComplete register a callback function that will be called when Promise is rejected or resolved. //If promise is already rejected or resolved, the callback will immediately called. //According to the status of Promise, value or error will be paramter of Always callback function. //Value is the paramter if Promise is resolved, or error is the paramter if Promise is rejected. //Always callback will be not called if Promise be called. func (this *Promise) OnComplete(callback func(v interface{})) *Promise { this.Future.OnComplete(callback) return this } //OnCancel registers a callback function that will be called when Promise is cancelled. //If promise is already cancelled, the callback will immediately called. func (this *Promise) OnCancel(callback func()) *Promise { this.Future.OnCancel(callback) return this } //NewPromise is factory function for Promise func NewPromise() *Promise { val := &futureVal{ make([]func(v interface{}), 0, 8), make([]func(v interface{}), 0, 8), make([]func(v interface{}), 0, 4), make([]func(), 0, 2), make([]*pipe, 0, 4), nil, } f := &Promise{ &Future{ rand.Int(), make(chan struct{}), unsafe.Pointer(val), }, } return f } ================================================ FILE: utils.go ================================================ package promise import ( "bytes" "errors" "fmt" "runtime" "strconv" ) //NoMatchedError presents no future that returns matched result in WhenAnyTrue function. type NoMatchedError struct { Results []interface{} } func (e *NoMatchedError) Error() string { return "No matched future" } func (e *NoMatchedError) HasError() bool { for _, ie := range e.Results { if _, ok1 := ie.(error); ok1 { return true } } return false } func newNoMatchedError(results []interface{}) *NoMatchedError { return &NoMatchedError{results} } func newNoMatchedError1(e interface{}) *NoMatchedError { return &NoMatchedError{[]interface{}{e}} } //AggregateError aggregate multi errors into an error type AggregateError struct { s string InnerErrs []error } func (e *AggregateError) Error() string { if e.InnerErrs == nil { return e.s } else { buf := bytes.NewBufferString(e.s) buf.WriteString("\n\n") for i, ie := range e.InnerErrs { if ie == nil { continue } buf.WriteString("error appears in Future ") buf.WriteString(strconv.Itoa(i)) buf.WriteString(": ") buf.WriteString(ie.Error()) buf.WriteString("\n") } buf.WriteString("\n") return buf.String() } } func newAggregateError(s string, innerErrors []error) *AggregateError { return &AggregateError{newErrorWithStacks(s).Error(), innerErrors} } func newAggregateError1(s string, e interface{}) *AggregateError { return &AggregateError{newErrorWithStacks(s).Error(), []error{getError(e)}} } func newErrorWithStacks(i interface{}) (e error) { err := getError(i) buf := bytes.NewBufferString(err.Error()) buf.WriteString("\n") pcs := make([]uintptr, 50) num := runtime.Callers(2, pcs) for _, v := range pcs[0:num] { fun := runtime.FuncForPC(v) file, line := fun.FileLine(v) name := fun.Name() //fmt.Println(name, file + ":", line) writeStrings(buf, []string{name, " ", file, ":", strconv.Itoa(line), "\n"}) } return errors.New(buf.String()) } func getAct(pr *Promise, act interface{}) (f func() (r interface{}, err error)) { var ( act1 func() (interface{}, error) act2 func(Canceller) (interface{}, error) ) canCancel := false //convert the act to the function that has return value and error if act function haven't return value and error switch v := act.(type) { case func() (interface{}, error): act1 = v case func(Canceller) (interface{}, error): canCancel = true act2 = v case func(): act1 = func() (interface{}, error) { v() return nil, nil } case func(Canceller): canCancel = true act2 = func(canceller Canceller) (interface{}, error) { v(canceller) return nil, nil } default: if e, ok := v.(error); !ok { pr.Resolve(v) } else { pr.Reject(e) } return nil } //If paramters of act function has a Canceller interface, the Future will can be cancelled. var canceller Canceller = nil if pr != nil && canCancel { //pr.EnableCanceller() canceller = pr.Canceller() } //return proxy function of act function f = func() (r interface{}, err error) { defer func() { if e := recover(); e != nil { err = newErrorWithStacks(e) } }() if canCancel { r, err = act2(canceller) } else { r, err = act1() } return } return } func startPipe(r *PromiseResult, pipeTask func(v interface{}) *Future, pipePromise *Promise) { //处理链式异步任务 if pipeTask != nil { f := pipeTask(r.Result) f.OnSuccess(func(v interface{}) { pipePromise.Resolve(v) }).OnFailure(func(v interface{}) { pipePromise.Reject(getError(v)) }) } } func getFutureReturnVal(r *PromiseResult) (interface{}, error) { if r.Typ == RESULT_SUCCESS { return r.Result, nil } else if r.Typ == RESULT_FAILURE { return nil, getError(r.Result) } else { return nil, getError(r.Result) //&CancelledError{} } } //执行回调函数 func execCallback(r *PromiseResult, dones []func(v interface{}), fails []func(v interface{}), always []func(v interface{}), cancels []func()) { if r.Typ == RESULT_CANCELLED { for _, f := range cancels { func() { defer func() { if e := recover(); e != nil { err := newErrorWithStacks(e) fmt.Println("error happens:\n ", err) } }() f() }() } return } var callbacks []func(v interface{}) if r.Typ == RESULT_SUCCESS { callbacks = dones } else { callbacks = fails } forFs := func(s []func(v interface{})) { forSlice(s, func(f func(v interface{})) { f(r.Result) }) } forFs(callbacks) forFs(always) } func forSlice(s []func(v interface{}), f func(func(v interface{}))) { for _, e := range s { func() { defer func() { if e := recover(); e != nil { err := newErrorWithStacks(e) fmt.Println("error happens:\n ", err) } }() f(e) }() } } //Error handling struct and functions------------------------------ type stringer interface { String() string } func getError(i interface{}) (e error) { if i != nil { switch v := i.(type) { case error: e = v case string: e = errors.New(v) default: if s, ok := i.(stringer); ok { e = errors.New(s.String()) } else { e = errors.New(fmt.Sprintf("%v", i)) } } } return } func writeStrings(buf *bytes.Buffer, strings []string) { for _, s := range strings { buf.WriteString(s) } }