Skip to Content
Course content

124: Timers and Tickers

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're in a kitchen. You have two tools for tracking time: a mechanical egg timer and a metronome. The egg timer is a "one-shot" deal. You twist it to three minutes, walk away, and when it dings, you're done. You don't expect it to ding again every three minutes for the rest of the afternoon. The metronome, however, is different. Once you start it, it clicks at a steady, rhythmic interval—every second, every second, every second—until you physically reach out and stop it.

In Go, time.Timer is your egg timer, and time.Ticker is your metronome. Both use channels to tell your code that time is up, but they serve very different architectural purposes.

The One-Shot Deal with Timers

Most of the time, you'll use a timer to handle timeouts. I've seen countless bugs where a program hangs forever because a network request never returned. That's where timers save you. If you just need a quick "do this after X duration," time.After is the shorthand way to go. It returns a channel that sends a single value after the time elapses.


package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("Waiting for the 'egg timer' to ding...")
	
	// time.After is great for simple select statements
	select {
	case <-time.After(2 * time.Second):
		fmt.Println("Time's up! The egg is boiled.")
	}
}

But here is the nuance: if you need to be able to stop the timer or reset it before it expires, time.After won't work because it doesn't give you a handle to the timer object. In those cases, you use time.NewTimer. This gives you a struct that allows you to call Stop(), which is crucial for preventing resource leaks in high-performance loops.

Steady Pulses with Tickers

Now, let's talk about the metronome. You use a time.Ticker when you have a recurring task—like sending a heartbeat signal to a server every 30 seconds or refreshing a cache every minute. Unlike a timer, a ticker's channel will fire repeatedly at the interval you specify.


package main

import (
	"fmt"
	"time"
)

func main() {
	// Create a ticker that clicks every 500 milliseconds
	ticker := time.NewTicker(500 * time.Millisecond)
	
	// I'm using a channel to tell the loop when to stop
	done := make(chan bool)

	go func() {
		for {
			select {
			case <-done:
				return
			case t := <-ticker.C:
				fmt.Println("Tick at", t)
			}
		}
	}()

	time.Sleep(2 * time.Second)
	done <- true
	fmt.Println("Stopped the metronome.")
}

Don't Forget to Stop the Clock

Here is a piece of advice I give every junior dev: always stop your tickers. I've seen production environments slowly eat up memory because developers created tickers inside a function, forgot to call ticker.Stop(), and left them running in the background long after the function returned.

The garbage collector can't always clean up a ticker because the runtime is still actively sending pulses to that channel. Whenever you use NewTicker or NewTimer, you should almost always defer ticker.Stop() immediately after the error check. It's a simple habit that saves you from a nightmare debugging session three weeks later.




📋 Practical Task

Building a Heartbeat Monitor with Timeouts

You need to build a "Heartbeat Monitor" that simulates a connection to a remote server. Your program should implement the following logic:

  • Create a time.Ticker that triggers every 2 seconds. Every time it ticks, it should attempt to "ping" the server (just print "Sending heartbeat...").
  • For each ping, create a time.Timer that acts as a timeout. If the "server" (which you will simulate using a random delay) doesn't respond within 1 second, print "Warning: Heartbeat timed out!".
  • The simulation should run for a total of 10 seconds and then shut down gracefully, ensuring the ticker is stopped.

Requirement: Use a select block to handle both the ticker's pulse and the timer's timeout. Use time.Sleep with a random duration to simulate the variable response time of the server.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.