Select
Promise.race vs Select
หัวข้อที่มีชื่อว่า “Promise.race vs Select”ใน TypeScript Promise.race รับ array ของ Promise และ resolve ด้วยตัวที่เสร็จก่อน:
const result = await Promise.race([ fetch('/api/primary'), fetch('/api/backup'),]);ใน Go select ทำแบบเดียวกันนี้กับ channels — รอหลาย channel operation พร้อมกัน แล้วทำงานกับตัวแรกที่พร้อม:
select {case msg := <-ch1: fmt.Println("from ch1:", msg)case msg := <-ch2: fmt.Println("from ch2:", msg)}select จะ block จนกว่าจะมีอย่างน้อยหนึ่ง case ที่ทำงานได้ ถ้าหลาย case พร้อมในจังหวะเดียวกัน Go จะสุ่มเลือกแบบ uniform — scheduling จึงยุติธรรมแต่ไม่ deterministic
Select Statement พื้นฐาน
หัวข้อที่มีชื่อว่า “Select Statement พื้นฐาน”// TypeScript: Promise.race for the first resultasync function withTimeout<T>( promise: Promise<T>, ms: number): Promise<T> { const timeout = new Promise<never>((_, reject) => setTimeout(() => reject(new Error("timeout")), ms) ); return Promise.race([promise, timeout]);}// Go: select picks the first ready channelpackage main
import "fmt"
func main() { ch1 := make(chan string, 1) ch2 := make(chan string, 1)
ch1 <- "one" ch2 <- "two"
// Use separate selects so output is deterministic select { case msg := <-ch1: fmt.Println("Received from ch1:", msg) } select { case msg := <-ch2: fmt.Println("Received from ch2:", msg) }}Default Case — Non-blocking Select
หัวข้อที่มีชื่อว่า “Default Case — Non-blocking Select”เพิ่ม branch default เพื่อให้ select คืนค่าทันทีเมื่อไม่มี channel ไหนพร้อม แทนที่จะ block:
// TypeScript: non-blocking check via Promise.race with null fallbackasync function tryReceive(): Promise<string | null> { return Promise.race([ getMessage(), Promise.resolve(null), // resolves immediately ]);}// Go: non-blocking select with defaultpackage main
import "fmt"
func main() { ch := make(chan string, 1)
select { case msg := <-ch: fmt.Println("Got:", msg) default: fmt.Println("No message available") // runs immediately }
ch <- "hello"
select { case msg := <-ch: fmt.Println("Got:", msg) default: fmt.Println("No message available") }}Select กับ Context Cancellation
หัวข้อที่มีชื่อว่า “Select กับ Context Cancellation”การใช้ select ที่พบบ่อยที่สุดในงานจริงคือฟังทั้งงานที่เข้ามาและ cancellation signal จาก context.Context ไปพร้อมกัน:
// TypeScript: AbortController for cooperative cancellationasync function processItems( items: string[], signal: AbortSignal) { for (const item of items) { if (signal.aborted) break; await processItem(item); }}// Go: select listening to both a jobs channel and ctx.Done()package main
import ( "context" "fmt" "sync")
func worker(ctx context.Context, jobs <-chan int, wg *sync.WaitGroup) { defer wg.Done() for { select { case j, ok := <-jobs: if !ok { return // channel was closed — no more work } fmt.Println("Processing job:", j) case <-ctx.Done(): fmt.Println("Worker cancelled:", ctx.Err()) return } }}package main
import "fmt"
func main() { ch1 := make(chan string, 1) ch2 := make(chan string, 1)
ch1 <- "hello" ch2 <- "world"
// Receive from ch1 first (deterministic: separate selects) select { case msg := <-ch1: fmt.Println("ch1:", msg) }
// Receive from ch2 select { case msg := <-ch2: fmt.Println("ch2:", msg) }
// default: non-blocking — ch1 is empty now select { case msg := <-ch1: fmt.Println("extra:", msg) default: fmt.Println("no more messages") }}Compiling on go.dev (Go 1.27)… offline? an in-browser runtime (~8 MB, Go 1.26) is used instead