Do something after Task death

I’d additionally recommend using a Task.Supervisor.

iex(1)> Supervisor.start_link([{Task.Supervisor, name: MyTaskSupervisor}], strategy: :one_for_one)
{:ok, #PID<0.114.0>}

iex(2)> Task.Supervisor.async_nolink(MyTaskSupervisor, fn -> :awesome_task end)
%Task{
  mfa: {:erlang, :apply, 2},
  owner: #PID<0.112.0>,
  pid: #PID<0.117.0>,
  ref: #Reference<0.987707504.2573795360.120005>
}

iex(3)> flush()
{#Reference<0.987707504.2573795360.120005>, :awesome_task},
 {:DOWN, #Reference<0.987707504.2573795360.120005>, :process, #PID<0.117.0>, :normal}

iex(4)> Task.Supervisor.async_nolink(MyTaskSupervisor, fn -> raise "uh oh" end)
%Task{
  mfa: {:erlang, :apply, 2},
  owner: #PID<0.112.0>,
  pid: #PID<0.120.0>,
  ref: #Reference<0.987707504.2573795360.120046>
}

iex(5)> flush()
{:DOWN, #Reference<0.987707504.2573795360.120178>, :process, #PID<0.129.0>,
 {%RuntimeError{message: "uh oh"}, ...}}

Docs for async_nolink/3 have some more information on this. Basically, if the task succeeds, you’ll receive a {task_ref, result} message. Regardless of success of failure, you’ll receive a {:DOWN, ref, :process, pid, reason} message. If it was a normal exit, that reason will be :normal. Otherwise, it was abnormal.

Your process kicking off these tasks can implement handle_info and pattern-match on these to broadcast the appropriate PubSub message.