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