Comparison to Our Original Approach Tuesday, March 26, 2024
… 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:
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
package main
import "context"
type Group struct {
errc chan error
cancel context . CancelCauseFunc
count int
}
func NewGroup ( cancel context . CancelCauseFunc ) * Group {
return & Group { errc : make ( chan error , 1 ), cancel : cancel }
}
func ( g * Group ) Go ( f func () error ) {
g . count ++
go func () {
g . errc <- f ()
}()
}
func ( g * Group ) Wait () error {
var err error
for range g . count {
if e := <- g . errc ; e != nil && err == nil {
err = e
g . cancel ( e )
}
}
return err
}
Making our function:
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
package main
import (
"context"
"fillmore-labs.com/blog/structured/pkg/task"
)
func doWork ( ctx context . Context ) error {
ctx , cancel := context . WithCancelCause ( ctx )
defer cancel ( nil )
g := NewGroup ( cancel )
g . Go ( func () error {
return task . Task ( ctx , "task1" , processingTime / 3 , nil )
})
g . Go ( func () error {
return task . Task ( ctx , "task2" , processingTime / 2 , errFail )
})
g . Go ( func () error {
return task . Task ( ctx , "task3" , processingTime , nil )
})
return g . Wait ()
}
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 .