task = Task.async(fn -> fetch_data_sync() end)
do_some_other_work()
Task.await(task) |> do_something_with_data()
So you can mix 'red' and 'blue' functions as much as you want. Problem solved.[1] Erlang/Elixir multitasking is often called preemptive, but in fact it is a little bit more subtle than that. It is however a good first approximation when writing code.
The same is technically true of Go. In practice, in both cases, unless you're backing to a lot of C code, or in the case of Go, manage to write a really tight loop that never gives the scheduler a chance to run, it is not something that comes up in practice very often. (I'm at a total of 0 after ~6 years of use of Erlang and Go. YMMV, since I never did use any oddball C extensions, but every month for both languages that's less of a restriction than it used to be.)
The platform is better designed and smart where you don't need red and blue function. You just have green functions (being a bit silly here with colors).
> [From Post] Async functions don’t compose in expressions because of the callbacks, have different error-handling, and can’t be used with try/catch or inside a lot of other control flow statements.
Good news again. Don't worry about that. Work sequentially as you need in inside each request/task. If you want to do multiple tasks in parallel, spawn multiple tasks. The Erlang folks (and Go-routine folks too but a bit in a different way) figured this out many years ago and have built large, complicated and reliable distributed systems (WhatsApp, smartphone<->internet gateways, databases etc...)
Here's a simple test for checking this: does the language provide a green-thread/coroutine abstraction, or do you work at the OS thread level:
In JS, C#, JVM languages, Python etc you work at the OS thread, so async operations need to be handled differently from synchronous one.
Elixir/Erlang, Go, Haskell etc provide green thread abstractions, so your green thread can block on an async operation without blocking the OS thread.
Elixir/Erlang is especially nice since each process (green thread) is completely isolated. This has multiple advantages:
i) when an erlang process (green thread) crashes, it doesn't take the other green threads on the OS thread down with it.
ii) garbage collection is per erlang process, not for the entire run-time. So the garbage collector doesn't halt everything when it runs. This makes it a really solid option for soft real-time systems
iii) stack trace is also per green thread, so you only see execution steps for the particular green thread in the stack trace, this makes debugging much much easier
iv) erlang processes can communicate with processes on other machines, this makes it a really good option for dist systems
The only drawback is that it's slow for cpu bound stuff, so i'd avoid it for anything that's computation intensive
disclaimer: elixir fanboy
In golang, by convention, you only ever use asynchronous functions that return values. So there is "only one color." (You also have the option of writing callback spaghetti or implementing promises, but why would you do that?)
In elixir, you have the option of blocking on a Task. So you can do the same thing you do in golang, if you want. I don't know enough about elixir to say what the culture is like around this.