Timers
Schedule a one-shot event with time.NewTimer and cancel it early with Stop.
A time.Timer fires once after a specified duration. It exposes a channel C that receives a value when the timer expires. Call Stop to cancel before it fires.
Create a timer that fires after two seconds. Block on timer.C to wait for it. Use Stop in a second goroutine to cancel a timer before it fires - Stop returns false if the timer already fired.
package main
import (
"fmt"
"time"
)
func main() {
t1 := time.NewTimer(2 * time.Second)
<-t1.C
fmt.Println("timer 1 fired")
t2 := time.NewTimer(time.Second)
go func() {
<-t2.C
fmt.Println("timer 2 fired")
}()
stopped := t2.Stop()
if stopped {
fmt.Println("timer 2 stopped before firing")
}
}time.AfterFunc runs a callback in a new goroutine when the timer expires - no channel receive required:
t := time.AfterFunc(500*time.Millisecond, func() {
fmt.Println("callback fired")
})
// t.Stop() cancels the callback if called before the timer firesWhen you need a one-shot delay inside a select and you may cancel early, prefer time.NewTimer over time.After:
timer := time.NewTimer(5 * time.Second)
defer timer.Stop() // prevent the timer goroutine from leaking
select {
case result := <-work:
fmt.Println("got result:", result)
case <-timer.C:
fmt.Println("timed out")
}In production
Always call timer.Stop() and drain the channel if needed when cancelling early - the GC does not collect a timer that is still running. A common leak pattern: creating a timer in a loop without stopping the previous one before resetting it. If Stop returns false (the timer already fired), drain the channel to unblock future receives: select { case <-t.C: default: }.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free