Skip to content

fanliao/go-promise

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

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()

Get the result of future

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

Merge multiple futures

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

Promise pipelining

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

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

Document

License

go-promise is licensed under the MIT Licence, (http://www.apache.org/licenses/LICENSE-2.0.html).

About

A library implement futrue and promise

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages