Comparison to Our Original Approach
Categories:
… continued from the previous post.
Refactor Our Original Approach
What we have done to the previously can also be done to the approach using an error channel:
1package main
2
3import "context"
4
5type Group struct {
6 errc chan error
7 cancel context.CancelCauseFunc
8 count int
9}
10
11func NewGroup(cancel context.CancelCauseFunc) *Group {
12 return &Group{errc: make(chan error, 1), cancel: cancel}
13}
14
15func (g *Group) Go(f func() error) {
16 g.count++
17 go func() {
18 g.errc <- f()
19 }()
20}
21
22func (g *Group) Wait() error {
23 var err error
24 for range g.count {
25 if e := <-g.errc; e != nil && err == nil {
26 err = e
27 g.cancel(e)
28 }
29 }
30
31 return err
32}
Making our function:
1package main
2
3import (
4 "context"
5
6 "fillmore-labs.com/blog/structured/pkg/task"
7)
8
9func doWork(ctx context.Context) error {
10 ctx, cancel := context.WithCancelCause(ctx)
11 defer cancel(nil)
12
13 g := NewGroup(cancel)
14
15 g.Go(func() error {
16 return task.Task(ctx, "task1", processingTime/3, nil)
17 })
18
19 g.Go(func() error {
20 return task.Task(ctx, "task2", processingTime/2, errFail)
21 })
22
23 g.Go(func() error {
24 return task.Task(ctx, "task3", processingTime, nil)
25 })
26
27 return g.Wait()
28}
As we see we get a nearly identical result for the main function, witht the API neatly abstracting our soulution. One
difference is that we have to call all subtasks asynchronously, since we need Group.Wait working on the error channel.
Summary
We have seen two approaches to structured concurrency with nearly identical APIs.
… continued in the next post.