Taskmaster v0.2.0 Taskmaster View Source

A set of convenience functions for concurrent, asynchronous tasks, loosely inspired by JavaScript's Promises.

Why?

While Elixir's Task module provides an API for easy creation of concurrent processes, it does so by blocking the caller process on calls to Task.await/2 or Task.async_stream/3. However, sometimes it is beneficial to operate asynchronously, in a manner somewhat similar to JavaScript's Promises - let the work be done in the background and then act on the results when everything is resolved.

Taskmaster wraps around the built-in Task module to provide a set of useful functions for doing just that.

Link to this section Summary

Functions

Creates a process, that runs funs concurrently and sends a message to the caller when all of them either return a value or one of them either crashes or returns an error.

Creates a process, that runs funs concurrently and when the first one resolves, sends a message to the caller.

Link to this section Types

Specs

options() :: [timeout: non_neg_integer(), link: boolean() | nil]

Link to this section Functions

Specs

all(funs :: [function(), ...], opts :: options()) :: {:ok, pid()}

Creates a process, that runs funs concurrently and sends a message to the caller when all of them either return a value or one of them either crashes or returns an error.

Possible messages:

  • {:all_results, results} when all the funs return a result
  • {:all_error, error} when either function:
    • returns an {:error, reason}
    • crashes
    • exceeds a :timeout option

The process created by all/2 by default isn't linked to the caller process. It can be started as a linked process by passing a link: true option.

Options

  • :timeout - a timeout for each function (defaults to 5000)
  • :link - should the started process by linked to the caller (defaults to false)

Example:

iex(1)> Taskmaster.all(
...(1)>   [
...(1)>     fn ->
...(1)>       :one
...(1)>     end,
...(1)>     fn ->
...(1)>       :timer.sleep(50)
...(1)>       :two
...(1)>     end,
...(1)>     fn ->
...(1)>       :timer.sleep(200)
...(1)>       :three
...(1)>     end
...(1)>   ],
...(1)>   timeout: 1000
...(1)> )
{:ok, #PID<0.216.0>}
iex(2)> flush()
{:all_return_values, [:one, :two, :three]}
:ok

Specs

race(funs :: [function(), ...], opts :: options()) :: {:ok, pid()}

Creates a process, that runs funs concurrently and when the first one resolves, sends a message to the caller.

Function resolves either by:

  • returning a value, which results in a {:race_won, value} message
  • crashing or returning a {:error, reason} tuple, which results in a {:race_interrupted, {:error | :exit, reason}} message
  • exceeding a :timeout options, which results in a {:race_interrupted, :timeout} message

The process created by race/2 by default isn't linked to the caller process. It can be started as a linked process by passing a link: true option.

Options

  • :timeout - a timeout for each function (defaults to 5000)
  • :link - should the started process by linked to the caller (defaults to false)

Example:

iex(1)> Taskmaster.race([
...(1)>         fn ->
...(1)>           :one
...(1)>         end,
...(1)>         fn ->
...(1)>           :timer.sleep(200)
...(1)>           :two
...(1)>         end,
...(1)>         fn ->
...(1)>           :timer.sleep(300)
...(1)>           :three
...(1)>         end
...(1)>       ])
{:ok, #PID<0.178.0>}
iex(2)> flush
{:race_won, :one}
:ok