I've been looking for an #elixir redux
-alike (primarly the event-bus stuff),
and have found that Phoenix.PubSub
paired with a GenServer
and a Task
supervisor seems to get the job done.
I did chase a few different constructions, like using Task.start
with
callbacks, but found that GenServer
doesn't have a predicable state when
callbacks are executed.
The following is scratched out, as there's opportunity to make it a bit more generic, like sending messages back to the original caller, etc.
objective: Schedule async work that can crash.
defmodule MyApp.Dispatcher do
use GenServer
@topic "MY_TOPIC"
def start_link(_) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def init(state \\ %{}) do
# subscribe to events emitted elsewhere
Phoenix.PubSub.subscribe(MyApp.PubSub, @topic)
# Start a Task.Supervisor
Task.Supervisor.start_link(name: MyApp.Dispatcher.TaskSupervisor)
{:ok, state}
end
# handle messages sent from dispatched tasks
def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do
next = Map.delete(state, pid)
# broadcast that a task is complete
PubSub.broadcast(MyApp.PubSub, @topic, {:tasks?, next})
{:noreply, next}
end
def handle_info({_event, %{id: _id}} = msg, state) do
task =
Task.Supervisor.async_nolink(MyApp.Dispatcher.TaskSupervisor, fn ->
# doing something async. It may or may not crash
# For instance, maybe this does a database write
end)
{:noreply, Map.put(state, task.pid, task)}
end
def handle_info(_, state) do
{:noreply, state}
end
end