Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ go get -u github.com/ghosind/go-async
import "github.com/ghosind/go-async"
```

> [!NOTE]
> 本工具集尚未稳定,后续版本可能会有不兼容的更改。

## 入门

下面的代码中,通过`All`函数并发执行函数直到全部执行完成,并返回它们的返回结果。
Expand Down Expand Up @@ -54,6 +57,7 @@ out, err := async.All(func (ctx context.Context) (int, error) {

- [`All`](https://pkg.go.dev/github.com/ghosind/go-async#All)
- [`AllCompleted`](https://pkg.go.dev/github.com/ghosind/go-async#AllCompleted)
- [`Fallback`](https://pkg.go.dev/github.com/ghosind/go-async#Fallback)
- [`Forever`](https://pkg.go.dev/github.com/ghosind/go-async#Forever)
- [`Parallel`](https://pkg.go.dev/github.com/ghosind/go-async#Parallel)
- [`ParallelCompleted`](https://pkg.go.dev/github.com/ghosind/go-async#ParallelCompleted)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ And then, import the library into your own code.
import "github.com/ghosind/go-async"
```

> [!NOTE]
> This library is not stable yet, anything may change in the later versions.

## Getting Started
Expand Down Expand Up @@ -56,6 +57,7 @@ For all functions, you can use the `XXXWithContext` function (like `AllWithConte

- [`All`](https://pkg.go.dev/github.com/ghosind/go-async#All)
- [`AllCompleted`](https://pkg.go.dev/github.com/ghosind/go-async#AllCompleted)
- [`Fallback`](https://pkg.go.dev/github.com/ghosind/go-async#Fallback)
- [`Forever`](https://pkg.go.dev/github.com/ghosind/go-async#Forever)
- [`Parallel`](https://pkg.go.dev/github.com/ghosind/go-async#Parallel)
- [`ParallelCompleted`](https://pkg.go.dev/github.com/ghosind/go-async#ParallelCompleted)
Expand Down
43 changes: 43 additions & 0 deletions fallback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package async

import "context"

// Fallback tries to run the functions in order until one function does not panic or return an
// error. It returns nil if one function succeeds, or returns the last error if all functions fail.
//
// err := async.Fallback(func() error {
// return errors.New("first error")
// }, func() error {
// return errors.New("second error")
// }, func() error {
// return nil
// }, func() error {
// return errors.New("third error")
// })
// // err: <nil>
func Fallback(fn AsyncFn, fallbacks ...AsyncFn) error {
return fallback(context.Background(), fn, fallbacks...)
}

// FallbackWithContext tries to run the functions in order with the specified context until one
// function does not panic or return an error. It returns nil if one function succeeds, or returns
// the last error if all functions fail.
func FallbackWithContext(ctx context.Context, fn AsyncFn, fallbacks ...AsyncFn) error {
return fallback(ctx, fn, fallbacks...)
}

// fallback runs the functions in order until one function does not panic or return an error.
func fallback(parent context.Context, fn AsyncFn, fallbacks ...AsyncFn) error {
funcs := append([]AsyncFn{fn}, fallbacks...)
ctx := getContext(parent)
var err error

for _, fn := range funcs {
_, err = invokeAsyncFn(fn, ctx, nil)
if err == nil {
return nil
}
}

return err
}
94 changes: 94 additions & 0 deletions fallback_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package async_test

import (
"context"
"errors"
"testing"

"github.com/ghosind/go-assert"
"github.com/ghosind/go-async"
)

func TestFallback(t *testing.T) {
a := assert.New(t)
runCount := 0

err := async.Fallback(
func() error {
runCount++
return errors.New("first error")
},
func() error {
runCount++
return nil
},
func() error {
runCount++
return errors.New("last error")
},
)
a.NilNow(err)
a.EqualNow(runCount, 2)
}

func TestFallbackAllFailed(t *testing.T) {
a := assert.New(t)
runCount := 0
finalErr := errors.New("third error")

err := async.Fallback(
func() error {
runCount++
return errors.New("first error")
},
func() error {
runCount++
return errors.New("second error")
},
func() error {
runCount++
return finalErr
},
)
a.IsErrorNow(err, finalErr)
a.EqualNow(runCount, 3)
}

func TestFallbackWithContext(t *testing.T) {
a := assert.New(t)
runCount := 0

err := async.FallbackWithContext(
context.Background(),
func() error {
runCount++
return errors.New("first error")
},
func() error {
runCount++
return nil
},
func() error {
runCount++
return errors.New("last error")
},
)
a.NilNow(err)
a.EqualNow(runCount, 2)
}

func ExampleFallback() {
err := async.Fallback(func() error {
return errors.New("first error")
}, func() error {
return errors.New("second error")
}, func() error {
return nil
}, func() error {
return errors.New("third error")
})
if err != nil {
// handle the error
}
// err: <nil>
}
Loading