01 Context
The context package in Go allows us to take action(mostly stop work) when the response to an API call is slow. This is one of the most frequent use case.
Let’s consider a scenario where a third party API call(fetchThirdPartyDataWhichCanBeSlow
) takes about five seconds to get the response.
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
package main
import (
"context"
"fmt"
"log"
"time"
)
func main() {
start := time.Now()
ctx := context.Background()
userID := 10
val, err := fetchUserData(ctx, userID)
if err != nil {
log.Fatal(err)
}
fmt.Println("result: ", val)
fmt.Println("took : ", time.Since(start))
}
func fetchUserData(ctx context.Context, userID int) (int, error) {
val, err := fetchThirdPartyDataWhichCanBeSlow()
if err != nil {
return 0, err
}
return val, nil
}
func fetchThirdPartyDataWhichCanBeSlow() (int, error) {
time.Sleep(time.Millisecond * 500)
return 666, nil
}
If we run the above code, the result would be like this.
Suppose that we want to control the response time limited to less than two seconds. To do so, we can utilize the context package as following.
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
package main
import (
"context"
"fmt"
"log"
"time"
)
func main() {
start := time.Now()
ctx := context.Background()
userID := 10
val, err := fetchUserData(ctx, userID)
if err != nil {
log.Fatal(err)
}
fmt.Println("result: ", val)
fmt.Println("took : ", time.Since(start))
}
type Response struct {
value int
err error
}
func fetchUserData(ctx context.Context, userID int) (int, error) {
ctx, cancel := context.WithTimeout(ctx, time.Millisecond*200)
defer cancel()
respch := make(chan Response)
go func() {
val, err := fetchThirdPartyDataWhichCanBeSlow()
respch <- Response{
value: val,
err: err,
}
}()
select {
case <-ctx.Done():
return 0, fmt.Errorf("fetching data from third party took too long")
case resp := <-respch:
return resp.value, resp.err
}
}
func fetchThirdPartyDataWhichCanBeSlow() (int, error) {
time.Sleep(time.Millisecond * 500)
return 666, nil
}
Give your focus on line 29 and 42 where the context magic takes place. On line 29 the context begins measuring time while the third party API call(fetchThirdPartyDataWhichCanBeSlow
) is triggered as a goroutine on line 33. At the select
statement on line 41, it is determined which case
should be returned. In this case, ctx.Done()
is triggered earlier than the API call, the code will return 0 with the error message. Below is the proof.
If we change the API call’s response time from five seconds to one on line 50, it will return the API response without triggering the context’s timeout.
I reference Anthony GG’s YouTube video to create this post. Please visit his channel for more detailed explanation.
How To Use The Context Package In Golang?