para.go (1518B)
1 package para 2 3 import ( 4 "context" 5 "fmt" 6 "iter" 7 "sync" 8 9 "golang.org/x/sync/errgroup" 10 ) 11 12 // Fanout processes a sequence concurrently. 13 func Fanout[V any](seq iter.Seq[V], fn func(value V)) { 14 wg := sync.WaitGroup{} 15 defer wg.Wait() 16 17 for v := range seq { 18 wg.Add(1) 19 go func() { 20 defer wg.Done() 21 fn(v) 22 }() 23 } 24 } 25 26 // Batch processes the sequence concurrently. Any error cancels the 27 // entire batch. No results are generated. This is helpful for a fanout of 28 // side-effects. 29 func Batch[T any](ctx context.Context, seq iter.Seq[T], work func(ctx context.Context, ii int, v T) error) error { 30 ctx, cancel := context.WithCancel(ctx) 31 defer cancel() 32 33 group, ctx := errgroup.WithContext(ctx) 34 35 ii := 0 36 for v := range seq { 37 func(ii int) { 38 group.Go(func() error { 39 return work(ctx, ii, v) 40 }) 41 }(ii) 42 ii += 1 43 } 44 45 return group.Wait() 46 } 47 48 // MapErr processes the sequence concurrently, transforming [In] to [Out]. 49 // Any error cancels the entire batch. This is helpful for a fanout of 50 // fallible transformations. 51 func MapErr[In any, Out any](ctx context.Context, seq []In, work func(ctx context.Context, ii int, v In) (Out, error)) ([]Out, error) { 52 ctx, cancel := context.WithCancel(ctx) 53 defer cancel() 54 55 group, ctx := errgroup.WithContext(ctx) 56 57 res := make([]Out, len(seq)) 58 59 for ii, v := range seq { 60 group.Go(func() error { 61 out, err := work(ctx, ii, v) 62 res[ii] = out 63 return err 64 }) 65 } 66 67 if err := group.Wait(); err != nil { 68 return nil, fmt.Errorf("map: %w", err) 69 } 70 71 return res, nil 72 }