Elixir Task implementation using processes


Elixir Task provides an abstraction over processes to perform long running jobs in parallel.

Let’s see first iteration.

Here we want to spawn a process and perform task in that process and collect back the result.

  1. We store the pid of spawning process to collect back the result.
  2. We spawn a function whose task is to send the result back to the spawning process after executing the passed function.
  3. We await for any message in the mailbox.

eg: [10000, 1000, 100] |> Enum.map(fn duration -> TaskiFirst.Taski.async(fn -> :timer.sleep(duration); duration end) end) |> Enum.map(fn _ -> TaskiFirst.Taski.await() end)

Here in our example function we make 3 processes sleep for appropriate amount of time. We get the result as [100, 1000, 10000].

We observe that the order of results are not the same as the order of execution. This happens because the receive block doesn’t care about who sent the message.

To solve this problem we identify receive with the spawned process id.

Here we are doing two changes.

  1. Instead of sending the result alone, we send a tuple with spawned process id and the result.
  2. We create receive blocks along with pid. Here the receive block waits till a matching pid is received. So the order is maintained.

eg: [10000, 1000, 100] |> Enum.map(fn duration -> Taski.Taski.async(fn -> :timer.sleep(duration); duration end) end) |> Enum.map(&Taski.Taski.await/1) will produce [10000, 1000, 100]

Here also a job can take longer, so it’s better to have an after block in receive with a timeout.

Check my book for Elixir starters at https://gum.co/XgbKSE

Self referencing automaton