diff --git a/README.md b/README.md index 97fd5df..9fb47e3 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ func main() { fmt.Println(val) } -func doThings() *future.Future { - fut, setResult := future.New() +func doThings() *future.Future[string] { + fut, setResult := future.New[string]() time.AfterFunc(10*time.Millisecond, func() { setResult("OK", nil) }) @@ -36,7 +36,7 @@ package main func main() { // Use sync function as async using future - fut := future.Call(func() (future.Value, error) { + fut := future.Call(func() (string, error) { return greet() }) @@ -62,7 +62,7 @@ func main() { done := make(chan struct{}) res := doThings() - res.Listen(func(val future.Value, err error) { + res.Listen(func(val string, err error) { fmt.Println(val) close(done) }) @@ -71,8 +71,8 @@ func main() { } // Future function -func doThings() *future.Future { - fut, setResult := future.New() +func doThings() *future.Future[string] { + fut, setResult := future.New[string]() time.AfterFunc(10*time.Millisecond, func() { setResult("OK", nil) }) diff --git a/future.go b/future.go index 979138f..bbf698b 100644 --- a/future.go +++ b/future.go @@ -5,30 +5,27 @@ import ( "sync" ) -// Value represents value concept. Can be anything. -type Value interface{} - // SetResultFunc is function to set result of the Future. -type SetResultFunc func(Value, error) +type SetResultFunc[T any] func(T, error) // Future holds the value of Future. -type Future struct { - val Value +type Future[T any] struct { + val T err error ready chan struct{} mu sync.Mutex - callbacks []SetResultFunc + callbacks []SetResultFunc[T] } // New constructs new Future. -func New() (*Future, SetResultFunc) { - f := &Future{ready: make(chan struct{})} +func New[T any]() (*Future[T], SetResultFunc[T]) { + f := &Future[T]{ready: make(chan struct{})} return f, f.setResult } // Get returns value when it's ready. Will return error when the ctx signal a cancelation. -func (f *Future) Get(ctx context.Context) (Value, error) { +func (f *Future[T]) Get(ctx context.Context) (T, error) { select { case <-f.ready: return f.val, f.err @@ -39,16 +36,17 @@ func (f *Future) Get(ctx context.Context) (Value, error) { case <-f.ready: return f.val, f.err case <-ctx.Done(): - return "", ctx.Err() + var zero T + return zero, ctx.Err() } } // Ready indicates whether result ready or not. -func (f *Future) Ready() <-chan struct{} { +func (f *Future[T]) Ready() <-chan struct{} { return f.ready } -func (f *Future) setResult(v Value, err error) { +func (f *Future[T]) setResult(v T, err error) { select { case <-f.ready: default: @@ -59,7 +57,7 @@ func (f *Future) setResult(v Value, err error) { } // Listen for the result. -func (f *Future) Listen(callback SetResultFunc) { +func (f *Future[T]) Listen(callback SetResultFunc[T]) { select { case <-f.ready: callback(f.val, f.err) @@ -70,7 +68,7 @@ func (f *Future) Listen(callback SetResultFunc) { } } -func (f *Future) notifyCallbacks() { +func (f *Future[T]) notifyCallbacks() { f.mu.Lock() for _, callback := range f.callbacks { callback(f.val, f.err) @@ -79,8 +77,8 @@ func (f *Future) notifyCallbacks() { } // Call will converts the sync function call as async call. -func Call(f func() (Value, error)) *Future { - fut, setDone := New() +func Call[T any](f func() (T, error)) *Future[T] { + fut, setDone := New[T]() go func() { res, err := f() setDone(res, err) diff --git a/future_test.go b/future_test.go index d5b06c3..f0ccf06 100644 --- a/future_test.go +++ b/future_test.go @@ -10,7 +10,7 @@ import ( ) func TestFuture_Get_immediate(t *testing.T) { - fut, setResult := future.New() + fut, setResult := future.New[string]() want := "Hello" setResult(want, nil) @@ -28,7 +28,7 @@ func TestFuture_Get_immediate(t *testing.T) { } func TestFuture_Listen_immediate(t *testing.T) { - fut, setResult := future.New() + fut, setResult := future.New[string]() want := "Hello" setResult(want, nil) @@ -36,8 +36,8 @@ func TestFuture_Listen_immediate(t *testing.T) { done := make(chan struct{}) var got string var gotErr error - fut.Listen(func(val future.Value, err error) { - got, gotErr = val.(string), err + fut.Listen(func(val string, err error) { + got, gotErr = val, err close(done) }) @@ -52,7 +52,7 @@ func TestFuture_Listen_immediate(t *testing.T) { } func TestFuture_Get_async(t *testing.T) { - fut, setResult := future.New() + fut, setResult := future.New[string]() want := "Hello" time.AfterFunc(10*time.Millisecond, func() { @@ -72,7 +72,7 @@ func TestFuture_Get_async(t *testing.T) { } func TestFuture_Get_timeout(t *testing.T) { - fut, _ := future.New() + fut, _ := future.New[string]() // no result @@ -87,8 +87,8 @@ func TestFuture_Get_timeout(t *testing.T) { // Basic usage of the future API. func ExampleFuture() { // Future function - doThings := func() *future.Future { - fut, setResult := future.New() + doThings := func() *future.Future[string] { + fut, setResult := future.New[string]() time.AfterFunc(10*time.Millisecond, func() { setResult("OK", nil) }) @@ -105,8 +105,8 @@ func ExampleFuture() { // Use Listen to get callback style. This can remove the need of extra gouroutine. func ExampleFuture_callback() { // Future function - doThings := func() *future.Future { - fut, setResult := future.New() + doThings := func() *future.Future[string] { + fut, setResult := future.New[string]() time.AfterFunc(10*time.Millisecond, func() { setResult("OK", nil) }) @@ -117,7 +117,7 @@ func ExampleFuture_callback() { done := make(chan struct{}) res := doThings() - res.Listen(func(val future.Value, err error) { + res.Listen(func(val string, err error) { fmt.Println(val) close(done) }) @@ -135,7 +135,7 @@ func ExampleCall() { } // Use sync function as async using future - fut := future.Call(func() (future.Value, error) { + fut := future.Call(func() (string, error) { return greet() })