How do you implement a timeout on a channel operation? Compare time.After, time.NewTimer, and context.
Question 198MediumGo 1.22 to 1.25
// 1. time.After: simplest, fine for one-off selects
select {
case v := <-ch:
use(v)
case <-time.After(2 * time.Second):
return errors.New("timeout")
}
// 2. Timer: reusable, stoppable
t := time.NewTimer(2 * time.Second)
defer t.Stop()
select {
case v := <-ch:
use(v)
case <-t.C:
return errors.New("timeout")
}
// 3. context: composes across call boundaries (preferred in services)
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
select {
case v := <-ch:
use(v)
case <-ctx.Done():
return ctx.Err() // context.DeadlineExceeded or Canceled
}
Trade-offs: time.After allocates a new timer on every call, which matters in hot loops (next question). NewTimer lets you Stop/Reset it. context is the idiomatic choice when the deadline should carry through to downstream calls (DB, HTTP) and cancel everything together. Interviewers want to hear "use context for request-scoped deadlines".
More on Channels & select
- Q196How does
for rangeover a channel work, and what are its pitfalls? - Q197What are directional channel types and why use them?
- Q199Does
time.Afterin a loop leak memory? How did Go 1.23 change this? - Q200What's the difference between
time.Tickerand repeatedtime.After? What happens if the consumer is slow? - Q201How do you do a non-blocking send or receive?
- Q202What does this print? (select evaluation order)