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)).