Skip to content

Commit

Permalink
Add generic support
Browse files Browse the repository at this point in the history
  • Loading branch information
uudashr committed Apr 5, 2024
1 parent dfe9adc commit 0f1e73f
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 35 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand All @@ -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()
})

Expand All @@ -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)
})
Expand All @@ -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)
})
Expand Down
32 changes: 15 additions & 17 deletions future.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
24 changes: 12 additions & 12 deletions future_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -28,16 +28,16 @@ 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)

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

Expand All @@ -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() {
Expand All @@ -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

Expand All @@ -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)
})
Expand All @@ -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)
})
Expand All @@ -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)
})
Expand All @@ -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()
})

Expand Down

0 comments on commit 0f1e73f

Please sign in to comment.