Timers in Crystal

We often want to execute code at some point in the future, or repeatedly at some interval. Crystal’s standard library provides features that make both of these tasks easy. We’ll look first at timers.

require "time"

# Timers represent a single event in the future. You
# tell the timer how long you want to wait, and it
# will execute a block of code after that time.
# This timer will wait 2 seconds.
timer1 = Timer.new(2.seconds) do
  puts "Timer 1 fired"
end

# The Timer#wait method blocks until the timer fires.
timer1.wait

# If you just wanted to wait, you could have used
# sleep. One reason a timer may be useful is
# that you can cancel the timer before it fires.
# Here's an example of that.
timer2 = Timer.new(1.second) do
  puts "Timer 2 fired"
end

spawn do
  timer2.wait
end

# Cancel the timer before it has a chance to fire
timer2.cancel
puts "Timer 2 stopped"

# Give the timer2 enough time to fire, if it ever
# was going to, to show it is in fact stopped.
sleep 2.seconds

The first timer will fire ~2s after we start the program, but the second should be stopped before it has a chance to fire.

$ crystal run timers.cr
Timer 1 fired
Timer 2 stopped

In this Crystal version:

  1. We use the Timer class from the standard library instead of Go’s time.Timer.
  2. Instead of channels, we pass a block to the Timer constructor which will be executed when the timer fires.
  3. We use Timer#wait to block until the timer fires, similar to <-timer.C in Go.
  4. We use spawn to create a new fiber (Crystal’s lightweight thread) instead of a goroutine.
  5. We use Timer#cancel to stop a timer, which is similar to Go’s timer.Stop().

The overall structure and functionality remain the same, demonstrating how to create, use, and cancel timers in Crystal.