Context are so useful that many libraries in the go ecosystem use them. You will also find them in applications that interact with remote servers, databases, API calls and more.
For this article, we will provide a beginner introduction to the context package in Go and how to use context for various use cases.
Context WithValue
One of the main use cases of contexts in Go is sharing data or using request-scoped values. The Golang context package provides you with the WithValue function that allows you to share data.
The function syntax is as shown:
func WithValue(parent Context, key, val interface{}) Context
The function takes a context, key and value as the parameters. The function will then create a new context based on the provided parent and add a specified value to the set key.
Think of it as an internal context with a key-value pair type inside. You can then fetch or retrieve values from the type.
Consider the example below that illustrates how to use the WithValue function and retrieve values from it.
import"fmt"
import"context"
funcmain() {
ctx := context.Background()
ctx = addValues(ctx)
retrieveValue(ctx)
}
funcaddValues(ctx context.Context) context.Context {
return context.WithValue(ctx, "key", "value")
}
funcretrieveValue(ctx context.Context) {
value := ctx.Value("key")
fmt.Println(value)
}
Focus on the main function of the code above. We start by creating a new context object, ctx, with the context.Background() function.
The background function will then return an empty non-nil context with no cancellations, no values, and no deadlines.
The background function serves as the initialization for incoming Context requests.
Context Set Values
The second part of the function is the addValues() function. We take a new context and bind it to an existing context in this example. The new context holds the key to add to the context.
Notice that the WithValue function returns a copy of the parent context and does not modify the existing context.
Context Retrieve Values
The second part of the program is the retrieveValue() function. In this case, we take the context struct and retrieve the values associated with the specified key.
If the key exists within the context, the function should return the associated value.
If the key does not exist, the function should return nil. We can implement conditional logic to check if the return value is nil and return an error if true.
Context With Timeout
Another common use case of contexts is handling timeouts. For example, you may need to define a timeout in certain applications if a specified action is not performed. This is incredibly simple to implement using the WithTimeout function from the context package. Let us look at how we can implement timeout using the context package.
import (
"context"
"fmt"
"time"
)
funcmain() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = addValues(ctx)
go retrieveValues(ctx)
select {
case<-ctx.Done():
fmt.Println("Took too long!")
}
time.Sleep(5 * time.Second)
}
funcaddValues(ctx context.Context) context.Context {
return context.WithValue(ctx, "key", "value")
}
funcretrieveValues(ctx context.Context) {
for {
select {
case<-ctx.Done():
fmt.Println("Timeout")
return
default:
value := ctx.Value("key")
fmt.Println(value)
}
time.Sleep(1 * time.Second)
}
}
In this example, we define a cancel function using the context.WithTimeout function. We can then trigger the cancel function manually if we see fit.
Once the timeout value is reached, the cancel function is called and runs the specified logic.
In our retrieve value function, we continuously retrieve the value with the specified key using the for loop. We also check if the Done channel from the main context is still alive. If the timeout has not exceeded, the parent context will still be open, but once the timeout is reached, the context is canceled.
In our example, the function will run every 1 second before the timeout is reached. An example output is as shown:
value
value
value
value
Took too long!
Timeout
Conclusion
This was a short and fundamental tutorial discussing contexts and how to use them in your Go programs. You can check the documentation to explore more about Go contexts.