64 / 68

context

context paketi

context — uzoq davom etadigan amallarni (masalan, tarmoq so'rovi, ma'lumotlar bazasiga murojaat) bekor qilish, ularga vaqt chegarasi (timeout) qo'yish va goroutine'lar orasida so'rovga tegishli ma'lumotni uzatish uchun ishlatiladigan standart paket.

Ayniqsa server dasturlarida juda muhim: masalan, foydalanuvchi brauzerni yopib qo'ysa yoki so'rov juda uzoq davom etsa, context yordamida server ortiqcha ishlashni to'xtatib, resurslarni bo'shatishi mumkin.

example.go
import "context"

1. Boshlang'ich context: context.Background()

Har qanday context zanjiri **context.Background()**dan boshlanadi — bu "bo'sh", hech narsaga bog'lanmagan context:

example.go
ctx := context.Background()

Odatda bu main() funksiyasida yoki eng yuqori darajadagi so'rov ishlov beruvchisida ishlatiladi, so'ngra undan boshqa maxsus context'lar "hosil qilinadi".


2. Vaqt chegarasi bilan context: WithTimeout()

Amalning ma'lum vaqtdan ko'p davom etishiga yo'l qo'ymaslik uchun context.WithTimeout() ishlatiladi:

example.go
package main

import (
    "context"
    "fmt"
    "time"
)

// vaqt talab qiladigan "sekin" amalni simulyatsiya qilamiz
func slowOperation(ctx context.Context, duration time.Duration) error {
    select {
    case <-time.After(duration): // amal shuncha vaqtdan keyin tugaydi
        fmt.Println("Amal muvaffaqiyatli tugadi")
        return nil
    case <-ctx.Done(): // yoki context muddati tugasa/bekor qilinsa
        return ctx.Err()
    }
}

func main() {
    // 100 millisekunddan ko'p kutmaydigan context yaratamiz
    ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
    defer cancel() // resurslarni bo'shatish uchun har doim chaqirilishi shart!

    // lekin amalning o'zi 500ms talab qiladi — demak, u tugamasdan bekor qilinadi
    err := slowOperation(ctx, 500*time.Millisecond)

    fmt.Println(err)
}

Natija:

kod
context deadline exceeded

[!IMPORTANT] WithTimeout() (va boshqa With...() funksiyalari) har doim cancel funksiyasini ham qaytaradi. Uni ishlatilmagan taqdirda ham defer cancel() orqali chaqirish shart — aks holda context bilan bog'liq ichki resurslar vaqtidan oldin bo'shatilmay, "sizib chiqishi" (leak) mumkin.


3. Qo'lda bekor qilish: WithCancel()

Ba'zan amalni aniq vaqt bo'yicha emas, balki boshqa bir hodisaga (masalan, foydalanuvchi "bekor qilish" tugmasini bosganda) qarab to'xtatish kerak bo'ladi:

example.go
package main

import (
    "context"
    "fmt"
    "time"
)

func slowOperation(ctx context.Context, duration time.Duration) error {
    select {
    case <-time.After(duration):
        fmt.Println("Amal muvaffaqiyatli tugadi")
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    // 50ms dan keyin amalni "tashqaridan" bekor qilamiz
    go func() {
        time.Sleep(50 * time.Millisecond)
        cancel()
    }()

    err := slowOperation(ctx, 500*time.Millisecond)

    fmt.Println(err)
}

Natija:

kod
context canceled

ctx.Err() qaysi sababdan to'xtatilganini aniq bildiradi: muddat tugagan bo'lsa — context deadline exceeded, qo'lda bekor qilingan bo'lsa — context canceled.


4. So'rovga tegishli ma'lumot uzatish: WithValue()

context.WithValue() orqali goroutine'lar zanjiri bo'ylab qo'shimcha ma'lumot (masalan, foydalanuvchi ID'si yoki so'rov identifikatori) uzatish mumkin:

example.go
package main

import (
    "context"
    "fmt"
)

// maxsus tur — oddiy string yoki int kalitlar boshqa paketlar bilan
// to'qnashib qolishining oldini olish uchun tavsiya etiladi
type contextKey string

func main() {
    ctx := context.WithValue(context.Background(), contextKey("userID"), 42)

    fmt.Println(ctx.Value(contextKey("userID")))
}

Natija:

kod
42

[!WARNING] context.WithValue()ni faqat so'rovga tegishli, ixtiyoriy ma'lumotlar (masalan, so'rov ID'si, autentifikatsiya ma'lumoti) uchun ishlating. Uni funksiyalarga oddiy parametr sifatida berilishi kerak bo'lgan ma'lumotlarni (masalan, funksiya hisoblashi uchun zarur bo'lgan qiymatlarni) "yashirincha" uzatish uchun ishlatish yomon amaliyot hisoblanadi — bu kodni o'qishni qiyinlashtiradi.


5. Nima uchun context HTTP serverlarda juda muhim

Amaliyotda context eng ko'p HTTP so'rovlar bilan ishlashda uchraydi. Har bir kelgan so'rov o'z contextiga ega bo'ladi (r.Context()), va agar foydalanuvchi ulanishni uzsa (masalan, brauzer sahifasini yopsa), bu context avtomatik bekor qilinadi:

example.go
func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    // uzoq davom etadigan amalga shu contextni uzatamiz
    err := slowDatabaseQuery(ctx)

    if err != nil {
        // agar foydalanuvchi allaqachon ulanishni uzgan bo'lsa,
        // serverimiz ortiqcha ishlamay, darhol to'xtaydi
        fmt.Println("So'rov bekor qilindi yoki xato:", err)
        return
    }
}

Bu usul serverning resurslarini behuda sarflamasligini ta'minlaydi — agar hech kim javobni kutmayotgan bo'lsa, server ham ishlashni davom ettirmaydi.


Xulosa: qachon qaysi funksiya kerak?

VazifaFunksiya
Boshlang'ich contextcontext.Background()
Vaqt chegarasi qo'yishcontext.WithTimeout(parent, davomiylik)
Aniq muddat qo'yish (masalan, "soat 18:00gacha")context.WithDeadline(parent, vaqt)
Qo'lda bekor qilish imkoniyaticontext.WithCancel(parent)
So'rovga tegishli ma'lumot uzatishcontext.WithValue(parent, kalit, qiymat)
Bekor qilingan/tugaganini tekshirishctx.Done() / ctx.Err()

[!NOTE] Amaliy qoida: agar funksiyangiz tarmoq so'rovi yuborsa, ma'lumotlar bazasiga murojaat qilsa yoki umuman uzoq davom etishi mumkin bo'lsa — unga birinchi parametr sifatida ctx context.Context qabul qilishni odat qiling. Bu Go community'sida keng qabul qilingan konvensiya.