select 用 default 做非阻塞,用 time.After 做逾時

寫 select 時要主動決定「等不到怎麼辦」。三種選擇:

寫法行為什麼時候用
什麼都不加等到天荒地老你確定一定會有資料來
default完全不等,立刻往下輪詢、非阻塞檢查
case <-time.After(N)最多等 N 秒最常用:呼叫外部服務、等 DB 回應
// 解法 1:default —— 沒資料就不等
select {
case msg := <-ch:
    fmt.Println("收到:", msg)
default:
    fmt.Println("現在沒資料,我不等了,繼續做別的事")
}
 
// 解法 2:time.After —— 給一個等待上限
select {
case msg := <-ch:
    fmt.Println("收到:", msg)
case <-time.After(2 * time.Second):
    fmt.Println("等了 2 秒還是沒來,放棄")
}

default 怎麼判斷要不要跳進去

select 執行的那一瞬間做一次檢查:掃過所有 case,看有沒有哪個 channel「現在立刻」有資料 → 有就執行(多個都有則隨機挑)→ 都沒有就看有沒有 default,有就執行 default 馬上往下走,沒有就停在這裡等。

這個檢查不會等待。default 就等於「非阻塞 select」。

⚠️ default 與 timeout 不要同時寫

select {
case msg := <-ch:
case <-time.After(2 * time.Second):   // 永遠輪不到
default:                               // 每次都走這裡
}

default 的話 select 根本不會等,那個 timeout case 沒有任何機會就緒。兩者是互斥的設計意圖:一個是「完全不等」,一個是「等但有上限」。

相關:select 等不到條件時會 deadlock 或 goroutine leakfor-select 裡的 time.After 每圈重置