How to handle multiple `start_async` tasks with the same key? `handle_async` is pruning pending tasks

I am building a spreadsheet kind Q&A system. I am using the code below. It starts multiple start_async(:get_answer, ...) calls to fetch answers asynchronously.

  defp assign_async_answer(socket, uuid, question) do
    socket
    |> assign(table: Table.put_uuid_value(socket.assigns.table, uuid, AsyncResult.loading()))
    |> start_async(:get_answer, fn ->
      {uuid, Prompts.get_answer(
        socket.assigns.company,
        question
      )}
    end)
  end

  def handle_event("cell_update", %{"ref" => ref, question: question}, socket) do
    socket =
      ref
      |> Table.get_uuids()
      |> Enum.reduce(socket, fn uuid, socket ->
        assign_async_answer(socket, uuid, question)
      end)

    {:noreply, socket}
  end

The above starts the multiple calls to Prompts.get_answer. However, the pending tasks are stopped as soon as the first answer is received. The pending start_async operations are stopped when the first handle_async(:get_answer, ...) is received.

I think it is because the handle_async call “prunes” the exiting calls with the same key.

What is the workaround? How should I go about in the above case? Please help.

1 Like

Same issue.
There’s no mention of pruning in the docs.
Should start_async be only used for things that are fired only once? Or is this a bug?

start_async uses the provided name to keep track of the task:

Starting two tasks with the same name means only the last one’s response will be successfully handled.

A general approach to things like this in Elixir is to expand the name from an atom to a tuple:

  defp assign_async_answer(socket, uuid, question) do
    socket
    |> assign(table: Table.put_uuid_value(socket.assigns.table, uuid, AsyncResult.loading()))
    |> start_async({:get_answer, uuid}, fn ->
      {uuid, Prompts.get_answer(
        socket.assigns.company,
        question
      )}
    end)
  end
{:key, id}

is exactly what I tried.
In phoenix_live_view 0.20.1 there was a is_atom(key) preventing me from doing that

So all I had to do is update to 0.20.2 :sweat_smile: since the is_atom(key) was removed.

I’d consider this solved. At least for me.

2 Likes