Skip to content

Commit e4f03f3

Browse files
authored
refactor!: store pointer to resulting value (#24)
1 parent 5394df3 commit e4f03f3

File tree

10 files changed

+172
-111
lines changed

10 files changed

+172
-111
lines changed

examples/future/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/reugn/async"
8+
"github.com/reugn/async/internal/util"
89
)
910

1011
func main() {
@@ -20,7 +21,7 @@ func asyncAction() async.Future[string] {
2021
promise := async.NewPromise[string]()
2122
go func() {
2223
time.Sleep(time.Second)
23-
promise.Success("OK")
24+
promise.Success(util.Ptr("OK"))
2425
}()
2526

2627
return promise.Future()

future.go

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,45 @@ import (
77
)
88

99
// Future represents a value which may or may not currently be available,
10-
// but will be available at some point, or an error if that value could not be made available.
10+
// but will be available at some point, or an error if that value could
11+
// not be made available.
1112
type Future[T any] interface {
1213

13-
// Map creates a new Future by applying a function to the successful result of this Future.
14-
Map(func(T) (T, error)) Future[T]
14+
// Map creates a new Future by applying a function to the successful
15+
// result of this Future.
16+
Map(func(*T) (*T, error)) Future[T]
1517

16-
// FlatMap creates a new Future by applying a function to the successful result of
17-
// this Future.
18-
FlatMap(func(T) (Future[T], error)) Future[T]
18+
// FlatMap creates a new Future by applying a function to the successful
19+
// result of this Future.
20+
FlatMap(func(*T) (Future[T], error)) Future[T]
1921

20-
// Join blocks until the Future is completed and returns either a result or an error.
21-
Join() (T, error)
22+
// Join blocks until the Future is completed and returns either a result
23+
// or an error.
24+
Join() (*T, error)
2225

23-
// Get blocks for at most the given time duration for this Future to complete
24-
// and returns either a result or an error.
25-
Get(time.Duration) (T, error)
26+
// Get blocks for at most the given time duration for this Future to
27+
// complete and returns either a result or an error.
28+
Get(time.Duration) (*T, error)
2629

27-
// Recover handles any error that this Future might contain using a resolver function.
28-
Recover(func() (T, error)) Future[T]
30+
// Recover handles any error that this Future might contain using a
31+
// resolver function.
32+
Recover(func() (*T, error)) Future[T]
2933

30-
// RecoverWith handles any error that this Future might contain using another Future.
34+
// RecoverWith handles any error that this Future might contain using
35+
// another Future.
3136
RecoverWith(Future[T]) Future[T]
3237

3338
// complete completes the Future with either a value or an error.
3439
// Is used by Promise internally.
35-
complete(T, error)
40+
complete(*T, error)
3641
}
3742

3843
// FutureImpl implements the Future interface.
3944
type FutureImpl[T any] struct {
4045
acceptOnce sync.Once
4146
completeOnce sync.Once
4247
done chan any
43-
value T
48+
value *T
4449
err error
4550
}
4651

@@ -62,7 +67,8 @@ func (fut *FutureImpl[T]) accept() {
6267
})
6368
}
6469

65-
// acceptTimeout blocks once, until the Future result is available or until the timeout expires.
70+
// acceptTimeout blocks once, until the Future result is available or until
71+
// the timeout expires.
6672
func (fut *FutureImpl[T]) acceptTimeout(timeout time.Duration) {
6773
fut.acceptOnce.Do(func() {
6874
timer := time.NewTimer(timeout)
@@ -82,40 +88,37 @@ func (fut *FutureImpl[T]) setResult(result any) {
8288
case error:
8389
fut.err = value
8490
default:
85-
fut.value = value.(T)
91+
fut.value = value.(*T)
8692
}
8793
}
8894

89-
// Map creates a new Future by applying a function to the successful result of this Future
90-
// and returns the result of the function as a new Future.
91-
func (fut *FutureImpl[T]) Map(f func(T) (T, error)) Future[T] {
95+
// Map creates a new Future by applying a function to the successful result
96+
// of this Future and returns the result of the function as a new Future.
97+
func (fut *FutureImpl[T]) Map(f func(*T) (*T, error)) Future[T] {
9298
next := NewFuture[T]()
9399
go func() {
94100
fut.accept()
95101
if fut.err != nil {
96-
var nilT T
97-
next.complete(nilT, fut.err)
102+
next.complete(nil, fut.err)
98103
} else {
99104
next.complete(f(fut.value))
100105
}
101106
}()
102107
return next
103108
}
104109

105-
// FlatMap creates a new Future by applying a function to the successful result of
106-
// this Future and returns the result of the function as a new Future.
107-
func (fut *FutureImpl[T]) FlatMap(f func(T) (Future[T], error)) Future[T] {
110+
// FlatMap creates a new Future by applying a function to the successful result
111+
// of this Future and returns the result of the function as a new Future.
112+
func (fut *FutureImpl[T]) FlatMap(f func(*T) (Future[T], error)) Future[T] {
108113
next := NewFuture[T]()
109114
go func() {
110115
fut.accept()
111116
if fut.err != nil {
112-
var nilT T
113-
next.complete(nilT, fut.err)
117+
next.complete(nil, fut.err)
114118
} else {
115119
tfut, terr := f(fut.value)
116120
if terr != nil {
117-
var nilT T
118-
next.complete(nilT, terr)
121+
next.complete(nil, terr)
119122
} else {
120123
next.complete(tfut.Join())
121124
}
@@ -124,22 +127,24 @@ func (fut *FutureImpl[T]) FlatMap(f func(T) (Future[T], error)) Future[T] {
124127
return next
125128
}
126129

127-
// Join blocks until the Future is completed and returns either a result or an error.
128-
func (fut *FutureImpl[T]) Join() (T, error) {
130+
// Join blocks until the Future is completed and returns either
131+
// a result or an error.
132+
func (fut *FutureImpl[T]) Join() (*T, error) {
129133
fut.accept()
130134
return fut.value, fut.err
131135
}
132136

133-
// Get blocks for at most the given time duration for this Future to complete
134-
// and returns either a result or an error.
135-
func (fut *FutureImpl[T]) Get(timeout time.Duration) (T, error) {
137+
// Get blocks for at most the given time duration for this Future to
138+
// complete and returns either a result or an error.
139+
func (fut *FutureImpl[T]) Get(timeout time.Duration) (*T, error) {
136140
fut.acceptTimeout(timeout)
137141
return fut.value, fut.err
138142
}
139143

140-
// Recover handles any error that this Future might contain using a given resolver function.
144+
// Recover handles any error that this Future might contain using
145+
// a given resolver function.
141146
// Returns the result as a new Future.
142-
func (fut *FutureImpl[T]) Recover(f func() (T, error)) Future[T] {
147+
func (fut *FutureImpl[T]) Recover(f func() (*T, error)) Future[T] {
143148
next := NewFuture[T]()
144149
go func() {
145150
fut.accept()
@@ -152,7 +157,8 @@ func (fut *FutureImpl[T]) Recover(f func() (T, error)) Future[T] {
152157
return next
153158
}
154159

155-
// RecoverWith handles any error that this Future might contain using another Future.
160+
// RecoverWith handles any error that this Future might contain using
161+
// another Future.
156162
// Returns the result as a new Future.
157163
func (fut *FutureImpl[T]) RecoverWith(rf Future[T]) Future[T] {
158164
next := NewFuture[T]()
@@ -168,7 +174,7 @@ func (fut *FutureImpl[T]) RecoverWith(rf Future[T]) Future[T] {
168174
}
169175

170176
// complete completes the Future with either a value or an error.
171-
func (fut *FutureImpl[T]) complete(value T, err error) {
177+
func (fut *FutureImpl[T]) complete(value *T, err error) {
172178
fut.completeOnce.Do(func() {
173179
go func() {
174180
if err != nil {

future_test.go

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,55 @@ import (
99
"time"
1010

1111
"github.com/reugn/async/internal/assert"
12+
"github.com/reugn/async/internal/util"
1213
)
1314

1415
func TestFuture(t *testing.T) {
1516
p := NewPromise[bool]()
1617
go func() {
17-
time.Sleep(time.Millisecond * 100)
18-
p.Success(true)
18+
time.Sleep(100 * time.Millisecond)
19+
p.Success(util.Ptr(true))
1920
}()
2021
res, err := p.Future().Join()
2122

22-
assert.Equal(t, res, true)
23-
assert.Equal(t, err, nil)
23+
assert.Equal(t, true, *res)
24+
assert.Equal(t, nil, err)
2425
}
2526

2627
func TestFutureUtils(t *testing.T) {
2728
p1 := NewPromise[int]()
2829
p2 := NewPromise[int]()
2930
p3 := NewPromise[int]()
31+
32+
res1 := util.Ptr(1)
33+
res2 := util.Ptr(2)
34+
err3 := errors.New("error")
35+
3036
go func() {
31-
time.Sleep(time.Millisecond * 100)
32-
p1.Success(1)
33-
time.Sleep(time.Millisecond * 200)
34-
p2.Success(2)
35-
time.Sleep(time.Millisecond * 300)
36-
p3.Success(3)
37+
time.Sleep(100 * time.Millisecond)
38+
p1.Success(res1)
39+
time.Sleep(200 * time.Millisecond)
40+
p2.Success(res2)
41+
time.Sleep(300 * time.Millisecond)
42+
p3.Failure(err3)
3743
}()
3844
arr := []Future[int]{p1.Future(), p2.Future(), p3.Future()}
39-
res := []any{1, 2, 3}
45+
res := []any{res1, res2, err3}
4046
futRes, _ := FutureSeq(arr).Join()
4147

42-
assert.Equal(t, res, futRes)
48+
assert.Equal(t, res, *futRes)
4349
}
4450

4551
func TestFutureFirstCompleted(t *testing.T) {
4652
p := NewPromise[bool]()
4753
go func() {
48-
time.Sleep(time.Millisecond * 1000)
49-
p.Success(true)
54+
time.Sleep(100 * time.Millisecond)
55+
p.Success(util.Ptr(true))
5056
}()
51-
timeout := FutureTimer[bool](time.Millisecond * 100)
57+
timeout := FutureTimer[bool](10 * time.Millisecond)
5258
futRes, futErr := FutureFirstCompletedOf(p.Future(), timeout).Join()
5359

54-
assert.Equal(t, false, futRes)
60+
assert.Equal(t, nil, futRes)
5561
if futErr == nil {
5662
t.Fatalf("futErr is nil")
5763
}
@@ -60,50 +66,79 @@ func TestFutureFirstCompleted(t *testing.T) {
6066
func TestFutureTransform(t *testing.T) {
6167
p1 := NewPromise[int]()
6268
go func() {
63-
time.Sleep(time.Millisecond * 100)
64-
p1.Success(1)
69+
time.Sleep(100 * time.Millisecond)
70+
p1.Success(util.Ptr(1))
6571
}()
66-
future := p1.Future().Map(func(v int) (int, error) {
67-
return v + 1, nil
68-
}).FlatMap(func(v int) (Future[int], error) {
69-
nv := v + 1
72+
future := p1.Future().Map(func(v *int) (*int, error) {
73+
inc := *v + 1
74+
return &inc, nil
75+
}).FlatMap(func(v *int) (Future[int], error) {
76+
inc := *v + 1
7077
p2 := NewPromise[int]()
71-
p2.Success(nv)
78+
p2.Success(&inc)
7279
return p2.Future(), nil
73-
}).Recover(func() (int, error) {
74-
return 5, nil
80+
}).Recover(func() (*int, error) {
81+
return util.Ptr(5), nil
7582
})
7683

7784
res, _ := future.Get(time.Second * 5)
78-
assert.Equal(t, 3, res)
85+
assert.Equal(t, 3, *res)
7986

8087
res, _ = future.Join()
81-
assert.Equal(t, 3, res)
88+
assert.Equal(t, 3, *res)
89+
}
90+
91+
func TestFutureRecover(t *testing.T) {
92+
p1 := NewPromise[int]()
93+
p2 := NewPromise[int]()
94+
go func() {
95+
time.Sleep(10 * time.Millisecond)
96+
p1.Success(util.Ptr(1))
97+
time.Sleep(10 * time.Millisecond)
98+
p2.Failure(errors.New("recover Future failure"))
99+
}()
100+
future := p1.Future().Map(func(v *int) (*int, error) {
101+
return nil, errors.New("map error")
102+
}).FlatMap(func(v *int) (Future[int], error) {
103+
p2 := NewPromise[int]()
104+
p2.Failure(errors.New("flatMap Future failure"))
105+
return p2.Future(), nil
106+
}).FlatMap(func(v *int) (Future[int], error) {
107+
return nil, errors.New("flatMap error")
108+
}).Recover(func() (*int, error) {
109+
return nil, errors.New("recover error")
110+
}).RecoverWith(p2.Future()).Recover(func() (*int, error) {
111+
return util.Ptr(2), nil
112+
})
113+
114+
res, err := future.Join()
115+
assert.Equal(t, 2, *res)
116+
assert.Equal(t, nil, err)
82117
}
83118

84119
func TestFutureFailure(t *testing.T) {
85120
p1 := NewPromise[int]()
86121
p2 := NewPromise[int]()
87122
go func() {
88-
time.Sleep(time.Millisecond * 100)
123+
time.Sleep(10 * time.Millisecond)
89124
p1.Failure(errors.New("Future error"))
90-
time.Sleep(time.Millisecond * 200)
91-
p2.Success(2)
125+
time.Sleep(20 * time.Millisecond)
126+
p2.Success(util.Ptr(2))
92127
}()
93128
res, _ := p1.Future().RecoverWith(p2.Future()).Join()
94129

95-
assert.Equal(t, 2, res)
130+
assert.Equal(t, 2, *res)
96131
}
97132

98133
func TestFutureTimeout(t *testing.T) {
99134
p := NewPromise[bool]()
100135
go func() {
101-
time.Sleep(time.Millisecond * 200)
102-
p.Success(true)
136+
time.Sleep(100 * time.Millisecond)
137+
p.Success(util.Ptr(true))
103138
}()
104139
future := p.Future()
105140

106-
_, err := future.Get(time.Millisecond * 50)
141+
_, err := future.Get(10 * time.Millisecond)
107142
assert.ErrorContains(t, err, "timeout")
108143

109144
_, err = future.Join()
@@ -121,21 +156,21 @@ func TestFutureGoroutineLeak(t *testing.T) {
121156
wg.Add(1)
122157
go func() {
123158
defer wg.Done()
124-
time.Sleep(time.Millisecond * 100)
125-
promise.Success("OK")
159+
time.Sleep(100 * time.Millisecond)
160+
promise.Success(util.Ptr("OK"))
126161
}()
127162
wg.Add(1)
128163
go func() {
129164
defer wg.Done()
130165
fut := promise.Future()
131-
_, _ = fut.Get(time.Millisecond * 10)
132-
time.Sleep(time.Millisecond * 100)
166+
_, _ = fut.Get(10 * time.Millisecond)
167+
time.Sleep(100 * time.Millisecond)
133168
_, _ = fut.Join()
134169
}()
135170
}
136171

137172
wg.Wait()
138-
time.Sleep(time.Millisecond * 10)
173+
time.Sleep(10 * time.Millisecond)
139174
numGoroutine := runtime.NumGoroutine()
140175
fmt.Println(numGoroutine)
141176
if numGoroutine > numFuture {

0 commit comments

Comments
 (0)