-
Notifications
You must be signed in to change notification settings - Fork 0
/
race.go
73 lines (63 loc) · 1.9 KB
/
race.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package async
import (
"context"
"sync/atomic"
)
// Race executes the functions asynchronously, it will return the index and the result of the first
// of the finished function (including panic), and it will not send a cancel signal to other
// functions.
//
// out, index, err := async.Race(func(ctx context.Context) (int, error) {
// request.Get("https://example.com")
// return 0, nil
// }, func(ctx context.Context) (string, error) {
// time.Sleep(500 * time.Millisecond)
// return "test", nil
// })
// // If the first function faster than the second one:
// // out: []any{0, <nil>}, index: 0, err: <nil>
// //
// // Otherwise:
// // out: []any{"test", <nil>}, index: 1, err: <nil>
func Race(funcs ...AsyncFn) ([]any, int, error) {
return race(context.Background(), funcs...)
}
// RaceWithContext executes the functions asynchronously, it will return the index and the result
// of the first of the finished function (including panic), and it will not send a cancel signal
// to other functions.
func RaceWithContext(ctx context.Context, funcs ...AsyncFn) ([]any, int, error) {
return race(ctx, funcs...)
}
// race executes the functions asynchronously, it will return the index and the result of the first
// of the finished function (including panic).
func race(ctx context.Context, funcs ...AsyncFn) ([]any, int, error) {
if len(funcs) == 0 {
return nil, -1, nil
}
validateAsyncFuncs(funcs...)
ctx = getContext(ctx)
finished := atomic.Bool{}
ch := make(chan executeResult)
defer close(ch)
for i := 0; i < len(funcs); i++ {
go func(n int) {
fn := funcs[n]
ret, err := invokeAsyncFn(fn, ctx, nil)
if finished.CompareAndSwap(false, true) {
ch <- executeResult{
Index: n,
Error: err,
Out: ret,
}
}
}(i)
}
ret := <-ch
if ret.Error != nil {
return ret.Out, ret.Index, &executionError{
index: ret.Index,
err: ret.Error,
}
}
return ret.Out, ret.Index, nil
}