Context
The context package provides a mechanism to manage the flow of execution across API boundaries. It can be used for canceling operations, setting deadlines and propagating values.
In node.js there are AsyncLocalStorage and AbortSignal.
Go's API is pretty dump, no magic. You have to passdown Context to child function calls explicitly.
It's worth mentioning that the API of the context package takes an
immutable approach. When creating a new context with
context.WithTimeout, context.WithDeadline, or context.WithValue,
a new context is returned, leaving the original context
unchanged, which makes the code more predictable.
package main
import (
"context"
"fmt"
"time"
)
func doWork(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("context canceled")
case <-time.After(time.Second):
fmt.Println("work done")
}
}
func foo(ctx context.Context) {
doWork(ctx)
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
go foo(ctx)
<-ctx.Done()
}
example: limit http request processing time
package main
import (
"fmt"
"net/http"
"time"
)
func TimeoutMiddleware(timeout time.Duration) func(http.HandlerFunc) http.HandlerFunc {
return func(next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
done := make(chan bool, 1)
go func() {
next(w, r)
done <- true
}()
select {
case <-time.After(timeout):
http.Error(w, "Request Timeout", http.StatusRequestTimeout)
case <-done:
}
})
}
}
func Handler(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second)
fmt.Fprintf(w, "Served on time")
}
func main() {
http.HandleFunc("GET /timeout/1", TimeoutMiddleware(1*time.Second)(Handler))
http.HandleFunc("GET /timeout/3", TimeoutMiddleware(3*time.Second)(Handler))
if err := http.ListenAndServe(":3000", nil); err != nil {
panic(err)
}
}