🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Timeout Patterns

A timeout is a limit on how long a caller waits for a result. When the limit passes, the caller stops waiting and uses a fallback instead.

A finished task yields its value

A task that computes 6 times 7 finishes at once. Yielding it returns its value in a success tuple.

task = Task.async(fn -> 6 * 7 end)
IO.inspect(Task.yield(task, 100))

An unfinished task yields nothing after the limit

A task that waits for a message that never arrives is still running at the limit. Yielding it returns nothing, and shutting it down ends it.

task = Task.async(fn -> receive do :never -> :done end end)
IO.inspect(Task.yield(task, 50))
Task.shutdown(task)
IO.inspect(Process.alive?(task.pid))

A timeout chooses a fallback value

A caller that gets nothing from yield uses a fallback instead of the missing value. A finished task keeps its real value.

fast = Task.async(fn -> 21 * 2 end)
slow = Task.async(fn -> receive do :never -> :done end end)
fast_result = case Task.yield(fast, 50) do {:ok, value} -> value; nil -> :fallback end
slow_result = case Task.yield(slow, 50) do {:ok, value} -> value; nil -> :fallback end
Task.shutdown(slow)
IO.inspect(fast_result)
IO.inspect(slow_result)

Example

fast1 = Task.async(fn -> 1 end)
fast2 = Task.async(fn -> 2 end)
slow = Task.async(fn -> receive do :never -> :done end end)
results = Task.yield_many([fast1, fast2, slow], 50)
outcomes = Enum.map(results, fn {_task, result} -> result end)
Enum.each(results, fn {task, result} -> if result == nil, do: Task.shutdown(task) end)
IO.inspect(outcomes)
IO.puts(length(outcomes))

Key points

  • Task: a unit of work that runs beside the caller.
  • Yield: a check that waits briefly for a task result and reports what it found.
  • Ready: a finished task answers with its value wrapped as a success tuple.
  • Late: an unfinished task answers with nothing, which the caller reads as a timeout.
  • Shutdown: the caller stops a late task so no work is left running.

In this example, three tasks start together, two finish and one waits for a message that never arrives. Yielding all three together returns two success tuples and nothing for the late task, and the late task is shut down before the outcomes and their count are printed.

Note

a timeout is a caller decision, not a failure inside the task itself.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A task is still running when its yield limit passes. What does Task.yield return?