Context — การยกเลิกและ Deadlines
AbortController vs context.Context
หัวข้อที่มีชื่อว่า “AbortController vs context.Context”ใน browser/Node.js คุณใช้ AbortController เพื่อยกเลิก fetch หรือ operation ใด ๆ ที่รับ AbortSignal:
const controller = new AbortController();setTimeout(() => controller.abort(), 5000); // timeout 5 วินาทีconst res = await fetch(url, { signal: controller.signal });ใน Go context.Context ทำหน้าที่เดียวกัน — และไปไกลกว่านั้น เพราะเป็น interface มาตรฐานที่พก cancellation signal, deadline (ถ้ามี) และ request-scoped value ติดตัวไปด้วย วิธีใช้คือส่ง context เป็น argument แรกของทุก I/O function ใน call chain การ cancel ที่ด้านบนสุดจะได้ส่งต่อลงไปทั้งสายโดยอัตโนมัติ
context.WithCancel
หัวข้อที่มีชื่อว่า “context.WithCancel”context.WithCancel คืน context ใหม่พร้อม cancel function การเรียก cancel จะ close channel ctx.Done() ซึ่งทุก goroutine ที่ฟัง context นั้นอยู่จะเห็น:
// TypeScript: AbortControllerconst controller = new AbortController();const { signal } = controller;
async function worker(signal: AbortSignal) { try { await doWork(signal); } catch (e) { if (e.name === 'AbortError') { console.log('Worker cancelled'); } }}
controller.abort(); // ยกเลิก// Go: context.WithCancelpackage main
import ( "context" "fmt" "sync")
func worker(ctx context.Context, id int, wg *sync.WaitGroup) { defer wg.Done() select { case <-ctx.Done(): // รอการยกเลิก fmt.Printf("Worker %d cancelled: %v\n", id, ctx.Err()) }}
func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup
for i := 1; i <= 3; i++ { wg.Add(1) go worker(ctx, i, &wg) }
cancel() // ยกเลิก workers ทั้งหมด wg.Wait() fmt.Println("All workers stopped")}context.WithTimeout
หัวข้อที่มีชื่อว่า “context.WithTimeout”สำหรับ operation ที่ต้องเสร็จภายใน deadline ใช้ context.WithTimeout Go จะ cancel context ให้อัตโนมัติเมื่อ timer หมดเวลา:
// TypeScript: timeout ด้วย Promise.raceasync function withTimeout<T>( fn: () => Promise<T>, ms: number): Promise<T> { const timeout = new Promise<never>((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms) ); return Promise.race([fn(), timeout]);}// Go: context.WithTimeoutpackage main
import ( "context" "fmt" "sync" "time")
func slowOperation(ctx context.Context, wg *sync.WaitGroup) { defer wg.Done() select { case <-time.After(5 * time.Second): // จำลอง slow operation fmt.Println("Operation complete") case <-ctx.Done(): fmt.Println("Operation timed out:", ctx.Err()) }}
func main() { // timeout หลัง 100ms ctx, cancel := context.WithTimeout( context.Background(), 100*time.Millisecond, ) defer cancel() // ดีที่สุดคือ defer cancel() เสมอ
var wg sync.WaitGroup wg.Add(1) go slowOperation(ctx, &wg) wg.Wait()}Context Values — ส่งข้อมูลผ่าน Call Chain
หัวข้อที่มีชื่อว่า “Context Values — ส่งข้อมูลผ่าน Call Chain”// TypeScript: ส่ง request-scoped data ผ่าน function argsasync function handler(req: Request) { const userId = req.headers.get('x-user-id'); await processRequest(userId);}// Go: context.WithValue สำหรับ request-scoped datapackage main
import ( "context" "fmt")
type contextKey string
const userIDKey contextKey = "userID"
func processRequest(ctx context.Context) { userID := ctx.Value(userIDKey) fmt.Println("Processing for user:", userID)}
func main() { ctx := context.WithValue( context.Background(), userIDKey, "user-123", ) processRequest(ctx)}package main
import ( "context" "fmt" "sync")
func worker(ctx context.Context, id int, wg *sync.WaitGroup) { defer wg.Done() select { case <-ctx.Done(): fmt.Printf("Worker %d stopped: %v\n", id, ctx.Err()) }}
func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup
for i := 1; i <= 3; i++ { wg.Add(1) go worker(ctx, i, &wg) }
cancel() wg.Wait() fmt.Println("All workers stopped")}Loading Go runtime (first run only, ~8 MB)…