go go/book/learninggo

Golang Context Deep Dive: From Zero to Hero - DEV Community

Go context package is designed to manage deadlines, cancellations, and request-scoped values across goroutines in concurrent applications.

Context provides a mechanism to control the lifecycle, cancellation, and propagation of requests across multiple goroutines.

It is particularly useful for controlling and coordinating multiple goroutines that are working on a shared task, such as handling HTTP requests or performing database operations.

Managing Goroutines with Context

package main
 
import (
	"context"
	"fmt"
	"time"
)
 
func main() {
	// Create a context with a timeout of 2 seconds
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel() // Cancel the context to free up resources
 
	// Launch a goroutine
	go func(ctx context.Context) {
		select {
		case <- time.After(3 * time.Second):
			fmt.Println("Task completed")
		case <- ctx.Done():
			fmt.Println("Task canceled:", ctx.Err())
		}
	}(ctx)
 
	// Wait for the goroutine to finish or timeout
	time.Sleep(3 * time.Second)
}

Using Context in Database Operations

package main
 
import (
	"context"
	"database/sql"
	"fmt"
	"time"
 
	_ "github.com/lib/pq"
)
 
func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
 
	db, err := sql.Open("postgres", "postgres://username:password@localhost/mydatabase?sslmode=disable")
	if err != nil {
		fmt.Println("Error connecting to the database:", err)
		return
	}
	defer db.Close()
 
	// Execute the database query with the custom context
	rows, err := db.QueryContext(ctx, "SELECT * FROM users")
	if err != nil {
		fmt.Println("Error executing query:", err)
		return
	}
	defer rows.Close()
 
	// Process query results
}

Using Context in HTTP Requests

package main
 
import (
	"context"
	"fmt"
	"net/http"
	"time"
)
 
func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
 
	req, err := http.NewRequestWithContext(ctx, "GET", "https://api.restful-api.dev/objects", nil)
	if err != nil {
		fmt.Println("Error creating request:", err)
		return
	}
 
	client := http.DefaultClient
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error making request:", err)
		return
	}
	defer resp.Body.Close()
 
	// Process response
}

In this example, we create a context with a timeout of 2 seconds using context.WithTimeout(). We then create an HTTP request with the custom context using http.NewRequestWithContext(). The context ensures that if the request takes longer than the specified timeout, it will be canceled.