Elixir: Task timeouts and cutoffs

Task.await has a default timeout of 5 seconds. If it doesn't complete by then, it raises an exception. To override the default, pass a value in milliseconds as the second argument:

task = Task.async(fn -> :timer.sleep(7000); "Finished!" end)

Task.await(task, 7000)

To wait indefinitely, use the :infinity atom:

Task.await(task, :infinity)

Task.await can only be called once for any given task, since it waits for a message to arrive for the given task. If I need to perform check that a task has completed, I can use Task.yield. It removes the message from the mailbox if it's completed. So Task.await is blocking, waits for the message, Task.yield simply checks if message exists.

iex> task = Task.async(fn -> :timer.sleep(8000); "Finished!" end)
iex> Task.yield(task, 5000)
nil
iex> Task.yield(task, 5000)
{:ok, "Finished!"}

So a long-running task can do behave differently if task is finished or not:

case Task.yield(task, 2000)
  {:ok, result} ->
    result
  nil ->
    Task.shutdown(task)
end

If the task hasn't completed in 2 seconds, then Task.shutdown shuts down the task. If the message arrives while shutting down, Task.shutdown will return {:ok, result}, else nil.

The :timer module has human-friendly ways for expressing time, converting each of the following to milliseconds:

iex> :timer.seconds(2)
2000

iex> :timer.minutes(5)
300000

iex> :timer.hours(5)
18000000

So I can do something like Task.yield(task, :timer.seconds(30)).