Using Task.start with receive -- is this the same as Task.async?

Hello! I am studying the Task module and I am trying to teach myself some patterns of concurrency in Elixir. I found something like this while reading the documentation for Process.sleep/1:

Task.start_link(fn ->
      Process.sleep(:timer.seconds(1))
      send(parent, :work_is_done)
end)

receive do
  :work_is_done -> IO.puts("Task complete!")
after
  2_000 -> IO.puts("Operation timed out.")
end

My question is: is this the same as using Task.async?


async_task = Task.async(fn ->
      Process.sleep(:timer.seconds(1))
      :work_is_done
end)

val = Task.await(async_task)
IO.inspect(val)
# :work_is_done

So this gives me 3 questions (related) that I hope someone can explain to me:

  1. Does Task.await function the same way as the send + receive example? (I like the syntax of Task.async and Task.await, but I want to make sure this is only syntax difference)
  2. I tried doing send/recieve from Task.start/1 and it seems to work the same as Task.start_link/1. Is there case where Task.start/1 must be used or case where Task.start_link/1 must be used?
  3. If I have a list of tasks, is it always better to use Task.async_stream? I tried using Enum.map to put many Task.asyncs together and it seems like Task.async_stream is a better way.

Thank you! I want to try to understand this well before I study GenServers.