Timeouts in Crystal
Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Crystal is straightforward using channels and select
.
require "time"
def main
# For our example, suppose we're executing an external
# call that returns its result on a channel `c1`
# after 2s. Note that the channel is unbuffered in Crystal,
# so we need to use a spawn block to prevent blocking.
c1 = Channel(String).new
spawn do
sleep 2.seconds
c1.send "result 1"
end
# Here's the `select` implementing a timeout.
# `c1.receive` awaits the result and `timeout` awaits
# a value to be sent after the timeout of 1s.
# Since `select` proceeds with the first receive that's ready,
# we'll take the timeout case if the operation takes more than
# the allowed 1s.
select
when res = c1.receive
puts res
when timeout(1.second)
puts "timeout 1"
end
# If we allow a longer timeout of 3s, then the receive
# from `c2` will succeed and we'll print the result.
c2 = Channel(String).new
spawn do
sleep 2.seconds
c2.send "result 2"
end
select
when res = c2.receive
puts res
when timeout(3.seconds)
puts "timeout 2"
end
end
main
Running this program shows the first operation timing out and the second succeeding.
$ crystal run timeouts.cr
timeout 1
result 2
In this Crystal version:
We use
require "time"
to access time-related functionality.Channels in Crystal are unbuffered by default, so we use
spawn
blocks to send messages asynchronously.The
select
statement in Crystal works similarly to Go, allowing us to wait on multiple channel operations.Instead of
time.After
, Crystal provides atimeout
macro that can be used directly inselect
statements.We define a
main
function and call it at the end of the file, as Crystal doesn’t have a specialmain
function like Go does.
This example demonstrates how to implement timeouts in Crystal using channels and select
, providing a way to bound execution time for operations that might take too long.