I can't understand the logic of this example in Go Concurrency Patterns: Context
func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
// Run the HTTP request in a goroutine and pass the response to f.
c := make(chan error, 1)
req = req.WithContext(ctx)
go func() { c <- f(http.DefaultClient.Do(req)) }()
select {
case <-ctx.Done():
<-c // Wait for f to return.
return ctx.Err()
case err := <-c:
return err
}
}
Before that it is said that:
The
Done
method returns a channel that acts as a cancellation signal to functions running on behalf of theContext
: when the channel is closed, the functions should abandon their work and return.
It looks like the equivalent of awaiting of <-c
only, without case <-ctx.Done()
at all. So, what is the point?
That context is canceled if the HTTP client disconnects or cancels the HTTP request (possible in HTTP/2). Passing an HTTP request's context to QueryWithTimeout above would cause the database query to stop early either if the overall HTTP request was canceled or if the query took more than five seconds.
It is thread safe: You can't modify the value of a context key once it has been set. The only way set another value for a given key is to create another context variable using context.
Background returns a non-nil, empty Context. It is never canceled, has no values, and has no deadline. It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests.
The context may cancel the Do
call. The case <-ctx.Done()
unambiguously identify that this happened.
Indeed, we may assume that the Do
call would then return a nil
http.Response
, and that the function f
would then return an error when this happens. But we wouldn’t know why f
received a nil
http.Response
as input argument.
The instruction case err := <-c:
handles the case when the error is produced inside f
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With