Timeouts in Elixir

Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Elixir is straightforward using processes and the receive block with an after clause.

defmodule Timeouts do
  def main do
    # For our example, suppose we're executing an external
    # call that returns its result after 2 seconds.
    # We'll use a separate process to simulate this.
    pid1 = spawn(fn ->
      :timer.sleep(2000)
      send(self(), {:result, "result 1"})
    end)

    # Here's the receive block implementing a timeout.
    # We'll wait for a message for 1 second. If no message
    # is received within that time, the after clause will execute.
    result = receive do
      {:result, res} -> res
    after
      1000 -> "timeout 1"
    end

    IO.puts(result)

    # If we allow a longer timeout of 3 seconds, then the receive
    # from the second process will succeed and we'll print the result.
    pid2 = spawn(fn ->
      :timer.sleep(2000)
      send(self(), {:result, "result 2"})
    end)

    result = receive do
      {:result, res} -> res
    after
      3000 -> "timeout 2"
    end

    IO.puts(result)
  end
end

Running this program shows the first operation timing out and the second succeeding.

$ elixir timeouts.exs
timeout 1
result 2

In this Elixir version:

  1. We use spawn to create separate processes that simulate long-running operations.
  2. Instead of channels, we use Elixir’s built-in message passing with send and receive.
  3. The receive block with an after clause replaces the select statement and time.After from the original example.
  4. We use :timer.sleep/1 (from Erlang) instead of time.Sleep.

This example demonstrates how to implement timeouts in Elixir, which is a crucial feature for handling long-running operations or external resource access in a robust manner.