Skip to content

Commit bb6e6f3

Browse files
authored
Merge pull request #5 from reugn/develop
v0.4.0
2 parents ee40c65 + 07131f5 commit bb6e6f3

File tree

11 files changed

+136
-135
lines changed

11 files changed

+136
-135
lines changed

.golangci.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
run:
22
skip-dirs:
3-
- generic
3+
- benchmarks
44

55
linters:
66
disable-all: true
@@ -15,20 +15,14 @@ linters:
1515
- gocyclo
1616
- gofmt
1717
- goimports
18-
- gosimple
1918
- govet
2019
- ineffassign
2120
- lll
2221
- misspell
2322
- prealloc
2423
- revive
25-
- staticcheck
26-
- structcheck
27-
- stylecheck
2824
- typecheck
2925
- unconvert
30-
- unparam
31-
- unused
3226
- varcheck
3327

3428
issues:
@@ -37,4 +31,3 @@ issues:
3731
- path: _test\.go
3832
linters:
3933
- errcheck
40-
- unparam

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@ The implemented patterns were taken from Scala and Java.
1313
## Overview
1414
* **Future** - A placeholder object for a value that may not yet exist.
1515
* **Promise** - While futures are defined as a type of read-only placeholder object created for a result which doesn’t yet exist, a promise can be thought of as a writable, single-assignment container, which completes a future.
16+
* **Task** - A data type for controlling possibly lazy and asynchronous computations.
1617
* **Reentrant Lock** - Mutex that allows goroutines to enter into the lock on a resource more than once.
1718
* **Optimistic Lock** - Mutex that allows optimistic reading. Could be retried or switched to RLock in case of failure. Significantly improves performance in case of frequent reads and short writes. See [benchmarks](./benchmarks/README.md).
1819

19-
### [Generic types](./generic)
20-
* **Task** - A data type for controlling possibly lazy and asynchronous computations.
21-
2220
## Examples
2321
Can be found in the examples directory/tests.
2422

examples/future/main.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
package main
22

33
import (
4-
"fmt"
4+
"log"
55
"time"
66

77
"github.com/reugn/async"
88
)
99

1010
func main() {
1111
future := asyncAction()
12-
rt, _ := future.Get()
13-
fmt.Println(rt)
12+
result, err := future.Get()
13+
if err != nil {
14+
log.Fatal(err)
15+
}
16+
log.Print(result)
1417
}
1518

16-
func asyncAction() async.Future {
17-
promise := async.NewPromise()
19+
func asyncAction() async.Future[string] {
20+
promise := async.NewPromise[string]()
1821
go func() {
1922
time.Sleep(time.Second)
2023
promise.Success("OK")
2124
}()
25+
2226
return promise.Future()
2327
}

future.go

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,66 +4,70 @@ import "sync"
44

55
// Future represents a value which may or may not currently be available,
66
// but will be available at some point, or an error if that value could not be made available.
7-
type Future interface {
7+
type Future[T any] interface {
88

99
// Map creates a new Future by applying a function to the successful result of this Future.
10-
Map(func(interface{}) (interface{}, error)) Future
10+
Map(func(T) (T, error)) Future[T]
1111

1212
// FlatMap creates a new Future by applying a function to the successful result of
1313
// this Future.
14-
FlatMap(func(interface{}) (Future, error)) Future
14+
FlatMap(func(T) (Future[T], error)) Future[T]
1515

1616
// Get blocks until the Future is completed and returns either a result or an error.
17-
Get() (interface{}, error)
17+
Get() (T, error)
1818

1919
// Recover handles any error that this Future might contain using a resolver function.
20-
Recover(func() (interface{}, error)) Future
20+
Recover(func() (T, error)) Future[T]
2121

2222
// RecoverWith handles any error that this Future might contain using another Future.
23-
RecoverWith(Future) Future
23+
RecoverWith(Future[T]) Future[T]
2424

2525
// complete completes the Future with either a value or an error.
2626
// Is used by Promise internally.
27-
complete(interface{}, error)
27+
complete(T, error)
2828
}
2929

3030
// FutureImpl implements the Future interface.
31-
type FutureImpl struct {
32-
acc sync.Once
33-
compl sync.Once
34-
done chan interface{}
35-
value interface{}
36-
err error
31+
type FutureImpl[T any] struct {
32+
acceptOnce sync.Once
33+
completeOnce sync.Once
34+
done chan interface{}
35+
value T
36+
err error
3737
}
3838

39+
// Verify FutureImpl satisfies the Future interface.
40+
var _ Future[any] = (*FutureImpl[any])(nil)
41+
3942
// NewFuture returns a new Future.
40-
func NewFuture() Future {
41-
return &FutureImpl{
43+
func NewFuture[T any]() Future[T] {
44+
return &FutureImpl[T]{
4245
done: make(chan interface{}),
4346
}
4447
}
4548

4649
// accept blocks once, until the Future result is available.
47-
func (fut *FutureImpl) accept() {
48-
fut.acc.Do(func() {
50+
func (fut *FutureImpl[T]) accept() {
51+
fut.acceptOnce.Do(func() {
4952
sig := <-fut.done
5053
switch v := sig.(type) {
5154
case error:
5255
fut.err = v
5356
default:
54-
fut.value = v
57+
fut.value = v.(T)
5558
}
5659
})
5760
}
5861

5962
// Map creates a new Future by applying a function to the successful result of this Future
6063
// and returns the result of the function as a new Future.
61-
func (fut *FutureImpl) Map(f func(interface{}) (interface{}, error)) Future {
62-
next := NewFuture()
64+
func (fut *FutureImpl[T]) Map(f func(T) (T, error)) Future[T] {
65+
next := NewFuture[T]()
6366
go func() {
6467
fut.accept()
6568
if fut.err != nil {
66-
next.complete(nil, fut.err)
69+
var nilT T
70+
next.complete(nilT, fut.err)
6771
} else {
6872
next.complete(f(fut.value))
6973
}
@@ -73,16 +77,18 @@ func (fut *FutureImpl) Map(f func(interface{}) (interface{}, error)) Future {
7377

7478
// FlatMap creates a new Future by applying a function to the successful result of
7579
// this Future and returns the result of the function as a new Future.
76-
func (fut *FutureImpl) FlatMap(f func(interface{}) (Future, error)) Future {
77-
next := NewFuture()
80+
func (fut *FutureImpl[T]) FlatMap(f func(T) (Future[T], error)) Future[T] {
81+
next := NewFuture[T]()
7882
go func() {
7983
fut.accept()
8084
if fut.err != nil {
81-
next.complete(nil, fut.err)
85+
var nilT T
86+
next.complete(nilT, fut.err)
8287
} else {
8388
tfut, terr := f(fut.value)
8489
if terr != nil {
85-
next.complete(nil, terr)
90+
var nilT T
91+
next.complete(nilT, terr)
8692
} else {
8793
next.complete(tfut.Get())
8894
}
@@ -92,15 +98,15 @@ func (fut *FutureImpl) FlatMap(f func(interface{}) (Future, error)) Future {
9298
}
9399

94100
// Get blocks until the Future is completed and returns either a result or an error.
95-
func (fut *FutureImpl) Get() (interface{}, error) {
101+
func (fut *FutureImpl[T]) Get() (T, error) {
96102
fut.accept()
97103
return fut.value, fut.err
98104
}
99105

100106
// Recover handles any error that this Future might contain using a given resolver function.
101107
// Returns the result as a new Future.
102-
func (fut *FutureImpl) Recover(f func() (interface{}, error)) Future {
103-
next := NewFuture()
108+
func (fut *FutureImpl[T]) Recover(f func() (T, error)) Future[T] {
109+
next := NewFuture[T]()
104110
go func() {
105111
fut.accept()
106112
if fut.err != nil {
@@ -114,8 +120,8 @@ func (fut *FutureImpl) Recover(f func() (interface{}, error)) Future {
114120

115121
// RecoverWith handles any error that this Future might contain using another Future.
116122
// Returns the result as a new Future.
117-
func (fut *FutureImpl) RecoverWith(rf Future) Future {
118-
next := NewFuture()
123+
func (fut *FutureImpl[T]) RecoverWith(rf Future[T]) Future[T] {
124+
next := NewFuture[T]()
119125
go func() {
120126
fut.accept()
121127
if fut.err != nil {
@@ -128,13 +134,13 @@ func (fut *FutureImpl) RecoverWith(rf Future) Future {
128134
}
129135

130136
// complete completes the Future with either a value or an error.
131-
func (fut *FutureImpl) complete(v interface{}, e error) {
132-
fut.compl.Do(func() {
137+
func (fut *FutureImpl[T]) complete(value T, err error) {
138+
fut.completeOnce.Do(func() {
133139
go func() {
134-
if e != nil {
135-
fut.done <- e
140+
if err != nil {
141+
fut.done <- err
136142
} else {
137-
fut.done <- v
143+
fut.done <- value
138144
}
139145
}()
140146
})

future_test.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@ import (
99
)
1010

1111
func TestFuture(t *testing.T) {
12-
p := NewPromise()
12+
p := NewPromise[bool]()
1313
go func() {
1414
time.Sleep(time.Millisecond * 100)
1515
p.Success(true)
1616
}()
1717
v, e := p.Future().Get()
18-
internal.AssertEqual(t, v.(bool), true)
18+
19+
internal.AssertEqual(t, v, true)
1920
internal.AssertEqual(t, e, nil)
2021
}
2122

2223
func TestFutureUtils(t *testing.T) {
23-
p1 := NewPromise()
24-
p2 := NewPromise()
25-
p3 := NewPromise()
24+
p1 := NewPromise[int]()
25+
p2 := NewPromise[int]()
26+
p3 := NewPromise[int]()
2627
go func() {
2728
time.Sleep(time.Millisecond * 100)
2829
p1.Success(1)
@@ -31,54 +32,58 @@ func TestFutureUtils(t *testing.T) {
3132
time.Sleep(time.Millisecond * 300)
3233
p3.Success(3)
3334
}()
34-
arr := []Future{p1.Future(), p2.Future(), p3.Future()}
35+
arr := []Future[int]{p1.Future(), p2.Future(), p3.Future()}
3536
res := []interface{}{1, 2, 3}
3637
futRes, _ := FutureSeq(arr).Get()
38+
3739
internal.AssertEqual(t, res, futRes)
3840
}
3941

4042
func TestFutureFirstCompleted(t *testing.T) {
41-
p := NewPromise()
43+
p := NewPromise[bool]()
4244
go func() {
4345
time.Sleep(time.Millisecond * 1000)
4446
p.Success(true)
4547
}()
46-
timeout := FutureTimer(time.Millisecond * 100)
48+
timeout := FutureTimer[bool](time.Millisecond * 100)
4749
futRes, futErr := FutureFirstCompletedOf(p.Future(), timeout).Get()
48-
internal.AssertEqual(t, nil, futRes)
50+
51+
internal.AssertEqual(t, false, futRes)
4952
if futErr == nil {
5053
t.Fatalf("futErr is nil")
5154
}
5255
}
5356

5457
func TestFutureTransform(t *testing.T) {
55-
p1 := NewPromise()
58+
p1 := NewPromise[int]()
5659
go func() {
5760
time.Sleep(time.Millisecond * 100)
5861
p1.Success(1)
5962
}()
60-
res, _ := p1.Future().Map(func(v interface{}) (interface{}, error) {
61-
return v.(int) + 1, nil
62-
}).FlatMap(func(v interface{}) (Future, error) {
63-
nv := v.(int) + 1
64-
p2 := NewPromise()
63+
res, _ := p1.Future().Map(func(v int) (int, error) {
64+
return v + 1, nil
65+
}).FlatMap(func(v int) (Future[int], error) {
66+
nv := v + 1
67+
p2 := NewPromise[int]()
6568
p2.Success(nv)
6669
return p2.Future(), nil
67-
}).Recover(func() (interface{}, error) {
70+
}).Recover(func() (int, error) {
6871
return 5, nil
6972
}).Get()
73+
7074
internal.AssertEqual(t, 3, res)
7175
}
7276

7377
func TestFutureFailure(t *testing.T) {
74-
p1 := NewPromise()
75-
p2 := NewPromise()
78+
p1 := NewPromise[int]()
79+
p2 := NewPromise[int]()
7680
go func() {
7781
time.Sleep(time.Millisecond * 100)
7882
p1.Failure(errors.New("Future error"))
7983
time.Sleep(time.Millisecond * 200)
8084
p2.Success(2)
8185
}()
8286
res, _ := p1.Future().RecoverWith(p2.Future()).Get()
87+
8388
internal.AssertEqual(t, 2, res)
8489
}

future_utils.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import (
55
"time"
66
)
77

8-
// FutureSeq reduces many Futures into a single Future
9-
func FutureSeq(futures []Future) Future {
10-
next := NewFuture()
8+
// FutureSeq reduces many Futures into a single Future.
9+
func FutureSeq[T any](futures []Future[T]) Future[[]interface{}] {
10+
next := NewFuture[[]interface{}]()
1111
go func() {
1212
seq := make([]interface{}, len(futures))
1313
for i, f := range futures {
@@ -25,26 +25,27 @@ func FutureSeq(futures []Future) Future {
2525

2626
// FutureFirstCompletedOf asynchronously returns a new Future to the result of the first Future
2727
// in the list that is completed. This means no matter if it is completed as a success or as a failure.
28-
func FutureFirstCompletedOf(futures ...Future) Future {
29-
next := NewFuture()
28+
func FutureFirstCompletedOf[T any](futures ...Future[T]) Future[T] {
29+
next := NewFuture[T]()
3030
go func() {
3131
for _, f := range futures {
32-
go func(future Future) {
32+
go func(future Future[T]) {
3333
next.complete(future.Get())
3434
}(f)
3535
}
3636
}()
3737
return next
3838
}
3939

40-
// FutureTimer returns Future that will have been resolved after given duration
41-
// useful for FutureFirstCompletedOf for timeout purposes
42-
func FutureTimer(d time.Duration) Future {
43-
next := NewFuture()
40+
// FutureTimer returns Future that will have been resolved after given duration;
41+
// useful for FutureFirstCompletedOf for timeout purposes.
42+
func FutureTimer[T any](d time.Duration) Future[T] {
43+
next := NewFuture[T]()
4444
go func() {
4545
timer := time.NewTimer(d)
4646
<-timer.C
47-
next.complete(nil, fmt.Errorf("FutureTimer %v timeout", d))
47+
var nilT T
48+
next.complete(nilT, fmt.Errorf("FutureTimer %v timeout", d))
4849
}()
4950
return next
5051
}

0 commit comments

Comments
 (0)