Ce face context.WithTimeout
context.WithTimeout(parinte, 2*time.Second) creează un context care expiră automat după 2 secunde. La expirare, ctx.Done() se închide și ctx.Err() devine context.DeadlineExceeded. Este echivalent cu WithDeadline(parinte, time.Now().Add(timeout)).
Funcțiile din biblioteca standard care primesc un context îl respectă: http.NewRequestWithContext, interogările database/sql, exec.CommandContext. Pui deci limita o singură dată și ea se aplică întregului lanț de apeluri.
Dacă părintele are deja un termen mai apropiat, rămâne acela: un copil nu poate primi mai mult timp decât părintele. Ca la WithCancel, apelează întotdeauna cancel (cu defer), ca să eliberezi temporizatorul dacă operația termină mai devreme.
Parametri
| Parametru | Descriere |
|---|---|
parent |
Contextul părinte. |
timeout |
Cât timp are operația la dispoziție (time.Duration). |
Ce returnează
Contextul cu limită de timp și funcția cancel.
Exemple
Exemplul 1: Interogare prea lentă
Funcția interogare simulează o bază de date. Cu limita de 100 ms, prima cerere reușește, a doua nu.
package main
import (
"context"
"errors"
"fmt"
"time"
)
func interogare(ctx context.Context, sql string, durata time.Duration) (string, error) {
select {
case <-time.After(durata):
return "rezultat pentru " + sql, nil
case <-ctx.Done():
return "", ctx.Err()
}
}
func main() {
for _, durata := range []time.Duration{20 * time.Millisecond, 500 * time.Millisecond} {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
rez, err := interogare(ctx, "SELECT * FROM comenzi", durata)
cancel()
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("Interogarea a depășit limita:", err)
continue
}
fmt.Println(rez)
}
}
Rezultat
rezultat pentru SELECT * FROM comenzi
Interogarea a depășit limita: context deadline exceeded
Exemplul 2: Copilul nu primește mai mult timp decât părintele
package main
import (
"context"
"fmt"
"time"
)
func main() {
parinte, cancel1 := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel1()
copil, cancel2 := context.WithTimeout(parinte, time.Hour)
defer cancel2()
dp, _ := parinte.Deadline()
dc, _ := copil.Deadline()
fmt.Println("Același termen ca părintele:", dp.Equal(dc))
<-copil.Done()
fmt.Println("Copilul a expirat:", copil.Err())
}
Rezultat
Același termen ca părintele: true
Copilul a expirat: context deadline exceeded
Exemplul 3: Cerere HTTP cu limită de timp
Contextul se dă cererii; dacă serverul nu răspunde în 3 secunde, Do returnează eroarea. Exemplul are nevoie de internet.
package main
import (
"context"
"fmt"
"net/http"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", "https://dexblog.ro/", nil)
if err != nil {
fmt.Println(err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("eroare:", err)
return
}
defer resp.Body.Close()
fmt.Println("Status:", resp.Status)
}
Rezultat posibil (diferă la fiecare rulare)
Status: 200 OK
Greșeli frecvente
- Creezi contextul cu limită în afara unei bucle și îl folosești pentru toate iterațiile: limita se aplică întregii bucle, nu fiecărei cereri. Creează câte unul pe iterație și apelează
cancella sfârșitul fiecăreia. - Pui
defer cancel()într-o buclă lungă: apelurile se adună până la ieșirea din funcție. În buclă apeleazăcancel()direct, ca în primul exemplu. - Compari eroarea cu
==după ce a fost împachetată de alte funcții; foloseșteerrors.Is(err, context.DeadlineExceeded).
Comenzi înrudite
context.WithDeadline: Creează un context care se anulează singur la un moment fix (ora limită), nu după o durată.context.WithTimeoutCause: Ca WithTimeout și WithDeadline, dar la expirare context.Cause(ctx) returnează eroarea ta, nu doar DeadlineExceeded.context.WithCancel: Creează un context copil și o funcție cancel; apelul cancel() anunță toate goroutine-urile care îl folosesc să se oprească.time.After: Returnează un canal care primește ora curentă după durata dată; folosit în select pentru limite de timp (timeout).errors.Is: Verifică dacă eroarea err este target sau o conține, căutând prin toate erorile împachetate cu %w sau Join.