Skip to content

Commit c6017d9

Browse files
committed
feat: add Fallback function
Signed-off-by: Chen Su <ghosind@gmail.com>
1 parent 40b4f2f commit c6017d9

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

fallback.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package async
2+
3+
import "context"
4+
5+
// Fallback tries to run the functions in order until one function does not panic or return an
6+
// error. It returns nil if one function succeeds, or returns the last error if all functions fail.
7+
//
8+
// err := async.Fallback(func() error {
9+
// return errors.New("first error")
10+
// }, func() error {
11+
// return errors.New("second error")
12+
// }, func() error {
13+
// return nil
14+
// }, func() error {
15+
// return errors.New("third error")
16+
// })
17+
// // err: <nil>
18+
func Fallback(fn AsyncFn, fallbacks ...AsyncFn) error {
19+
return fallback(context.Background(), fn, fallbacks...)
20+
}
21+
22+
// FallbackWithContext tries to run the functions in order with the specified context until one
23+
// function does not panic or return an error. It returns nil if one function succeeds, or returns
24+
// the last error if all functions fail.
25+
func FallbackWithContext(ctx context.Context, fn AsyncFn, fallbacks ...AsyncFn) error {
26+
return fallback(ctx, fn, fallbacks...)
27+
}
28+
29+
// fallback runs the functions in order until one function does not panic or return an error.
30+
func fallback(parent context.Context, fn AsyncFn, fallbacks ...AsyncFn) error {
31+
funcs := append([]AsyncFn{fn}, fallbacks...)
32+
ctx := getContext(parent)
33+
var err error
34+
35+
for _, fn := range funcs {
36+
_, err = invokeAsyncFn(fn, ctx, nil)
37+
if err == nil {
38+
return nil
39+
}
40+
}
41+
42+
return err
43+
}

fallback_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package async_test
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"github.com/ghosind/go-assert"
9+
"github.com/ghosind/go-async"
10+
)
11+
12+
func TestFallback(t *testing.T) {
13+
a := assert.New(t)
14+
runCount := 0
15+
16+
err := async.Fallback(
17+
func() error {
18+
runCount++
19+
return errors.New("first error")
20+
},
21+
func() error {
22+
runCount++
23+
return nil
24+
},
25+
func() error {
26+
runCount++
27+
return errors.New("last error")
28+
},
29+
)
30+
a.NilNow(err)
31+
a.EqualNow(runCount, 2)
32+
}
33+
34+
func TestFallbackAllFailed(t *testing.T) {
35+
a := assert.New(t)
36+
runCount := 0
37+
finalErr := errors.New("third error")
38+
39+
err := async.Fallback(
40+
func() error {
41+
runCount++
42+
return errors.New("first error")
43+
},
44+
func() error {
45+
runCount++
46+
return errors.New("second error")
47+
},
48+
func() error {
49+
runCount++
50+
return finalErr
51+
},
52+
)
53+
a.IsErrorNow(err, finalErr)
54+
a.EqualNow(runCount, 3)
55+
}
56+
57+
func TestFallbackWithContext(t *testing.T) {
58+
a := assert.New(t)
59+
runCount := 0
60+
61+
err := async.FallbackWithContext(
62+
context.Background(),
63+
func() error {
64+
runCount++
65+
return errors.New("first error")
66+
},
67+
func() error {
68+
runCount++
69+
return nil
70+
},
71+
func() error {
72+
runCount++
73+
return errors.New("last error")
74+
},
75+
)
76+
a.NilNow(err)
77+
a.EqualNow(runCount, 2)
78+
}
79+
80+
func ExampleFallback() {
81+
err := async.Fallback(func() error {
82+
return errors.New("first error")
83+
}, func() error {
84+
return errors.New("second error")
85+
}, func() error {
86+
return nil
87+
}, func() error {
88+
return errors.New("third error")
89+
})
90+
if err != nil {
91+
// handle the error
92+
}
93+
// err: <nil>
94+
}

0 commit comments

Comments
 (0)