Timeouts in Nim
Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Nim is straightforward using channels and the select
statement.
import std/[asyncdispatch, asyncfutures, strformat]
proc main() {.async.} =
# For our example, suppose we're executing an external
# call that returns its result on a channel `c1`
# after 2s. Note that we're using an AsyncChannel,
# which is similar to a buffered channel in other languages.
let c1 = newAsyncChannel[string](1)
asyncCheck (proc() {.async.} =
await sleepAsync(2000)
await c1.send("result 1")
)()
# Here's the `select` implementing a timeout.
# We use `race` to await multiple futures concurrently.
# The first completed future will be the result.
let res = await race(
c1.recv(),
sleepAsync(1000)
)
if res.index == 0:
echo res.read(string)
else:
echo "timeout 1"
# If we allow a longer timeout of 3s, then the receive
# from `c2` will succeed and we'll print the result.
let c2 = newAsyncChannel[string](1)
asyncCheck (proc() {.async.} =
await sleepAsync(2000)
await c2.send("result 2")
)()
let res2 = await race(
c2.recv(),
sleepAsync(3000)
)
if res2.index == 0:
echo res2.read(string)
else:
echo "timeout 2"
waitFor main()
Running this program shows the first operation timing out and the second succeeding.
$ nim c -r timeouts.nim
timeout 1
result 2
In this Nim version, we use the asyncdispatch
module to handle asynchronous operations. The AsyncChannel
is used instead of Go’s buffered channels, and the race
procedure simulates the select
statement’s behavior. The asyncCheck
procedure is used to run asynchronous procedures concurrently, similar to goroutines in Go.
The overall structure and logic remain the same, with timeouts implemented using sleepAsync
and race
. This demonstrates how Nim can handle concurrent operations and timeouts in a manner similar to Go, albeit with slightly different syntax and concepts.