The context.Background function creates an empty context like context.TODO does, but it’s designed to be used where you intend to start a known context. Fundamentally the two functions do the same thing:
- They return an empty context that can be used as a
context.Context.
The biggest difference is how you signal your intent to other developers. If you’re unsure which one to use, context.Background is a good default option.
Using Data Within a Context
One benefit of using context.Context in a program is the ability to access data stored inside a context. By adding data to a context and passing the context from function to function, each layer of a program can add additional information about what’s happening.
- For example, the first function may add a username to the context. The next function may add the file path to the content the user is trying to access. Finally, a third function could then read the file from the system’s disk and log whether it was loaded successfully or not as well as which user tried to load it.
To add a new value to a context, use the context.WithValue function in the contextpackage. The function accepts three parameters: the parent context.Context, the key, and the value.Once you have a context.Context with a value added to it, you can access those values using the Valuemethod of a context.Context. Providing the Value method with a key will return the value stored.
When using contexts, it’s important to know that the values stored in a specific context.Context
are immutable, meaning they can’t be changed.
Ending a Context
Another powerful tool context.Context provides is a way to signal to any functions using it that the context has ended and should be considered complete.
Determining if a Context is Done
func main(){
ctx := context.Background()
ctx, stop := context.WithCancel(ctx)
// OR ctx, stop := context.WithTimeout(ctx, time.Second*3)
// OR ctx, stop := context.WithDeadline(ctx, time.Now().Add(time.Second*5))
// use the context to do some work in the background
go doSomething(ctx)
// other operation that results in a error
if err != nil {
stop() // send the cancellation signal to all functions using ctx
}
}
// (...)
func doSomething(ctx context.Context) error {
for {
// do something
select {
case <-ctx.Done(): // closes when the caller cancels the ctx
return ctx.Err() // has a value on context cancellation
}
}
}Giving a Context a Deadline
func doSomething(ctx context.Context) {
deadline := time.Now().Add(1500 * time.Millisecond)ctx, cancelCtx := context.WithDeadline(ctx, deadline)defer cancelCtx()
printCh := make(chan int)
go doAnother(ctx, printCh)
for num := 1; num <= 3; num++ {
select {case printCh <- num:time.Sleep(1 * time.Second)case <-ctx.Done():break}}
cancelCtx()
time.Sleep(100 * time.Millisecond)
fmt.Printf("doSomething: finished\n")
}
...Giving a Context a Time Limit
func doSomething(ctx context.Context) {
ctx, cancelCtx := context.WithTimeout(ctx, 1500*time.Millisecond)defer cancelCtx()
...
}https://www.digitalocean.com/community/tutorials/how-to-use-contexts-in-go