dopatraman

dopatraman

What do GenServers offer over Tasks?

I’m looking for clarity on the benefits of GenServers. At first I thought they could be used to process asynchronous work via handle_case, but it seems like the popular literature, some by Jose himself, advocates against using casts in favor of the synchronous call. If GenServers are meant to be used to handle synchronous code, what’s the point? I have trouble how I’d benefit more by using them over a Task.

Consider the following classic breakfast scenario. To have breakfast, I need to do the following things:

  1. Make toast
  2. Cook eggs
  3. Butter the toast
  4. Pour orange juice
  5. Plate the eggs and toast
  6. Eat

To do this, (1,3), 2, and 4 could be run as separate tasks. 5 could await 1, 2, and 3, and 6 could await 1-5:

oj_task = Task.async(fn -> pour_oj() end)
[
Task.async(fn -> make_toast() |> butter_toast end),
Task.async(fn -> make_eggs() end)
]
|> Enum.map(&Task.await/1)

plate_task = plate_eggs_and_toast()
[ oj_task, plate_task] |> Enum.map(&Task.await/1) |> eat()

How would I be able to do this via GenServers?

It’s possible that I’m missing some foundational understanding here. I just don’t see the utility of using call when a Task or cast would do the trick. Please help clarify. Thank you!

Most Liked

wojtekmach

wojtekmach

Hex Core Team

Anything you can do with Task you can do with a GenServer and the reason is… Task is a GenServer!

As others have mentioned, tasks are all about performing some computation, it has some initial state, does the work and returns the result. It doesnt maintain state in the sense that you cannot really ask it what its state is until the Task finishes. Theres another abstraction worth mentioning and that is Agent. Agent is the opposite of Task, its all about state and hardly about compute. You can ask the Agent what it state is and ask it to update its state but there is not really any computation associated with it.

Given both Agent and Task are built on top of a GenServer, it needs to necessarily allow handling both the state and compute. And it sure does!

Oh, and since you have been using async/await, what async basically does is it spawns a process (spawning a process is always async) to do the work and when the work is done it will send the message to the calling process. All awat basically does it is just waiting on that message. Thats basically what GenServer.call does too, it sends the message and awaits response. The difference is after async but before await you can do more work in the caller which is different than GenServer.call which is just one invocation. Sometimes you need one and sometimes the other is most appropriate.

Hope it helps!

ityonemo

ityonemo

Unless I’m mistaken, Task is not built on top of GenServer:

It’s really a new addition to OTP that Elixir brings to the table.

A task is for any situation where you want to have OTP supervision for things that are simple, linear sets of actions. You should default to using Tasks over GenServers in the general case.

GenServers have re-entrant, nonlinear asynchronous, event flows driven by message-passing. They’re very complicated and actually rather disorganized, code-wise, in OTP. 99% of the time you probably shouldn’t use a GenServer. Important exceptions are:

  • You’re handling a stateful communications protocol (like TCP or something higher level like DHCP which is stateful over a stateless protocol).
  • You’re modeling something stateful in the real world (for example a VM, or a client’s web browser panel) and need a “smart caching layer”
  • You’re modeling something virtual that needs to be stateful and respond specifically to messages with a nonlinear control flow (like a poker game)
gregvaughn

gregvaughn

EVERY process is sequential. A Task is only processing the function given to it. A GenServer is taking one message at a time from its mailbox. call vs. cast does not change this. Call vs. cast is whether the caller is waiting for a response from the GenServer or not.

edit: Sorry I didn’t mean to reply to you directly @krstfk, just to the topic in general

Where Next?

Popular in Discussions Top

blackode
Elixir Upgrading is so Simple in Ubuntu and It worked for me Ubuntu 16.04 git clone https://github.com/elixir-lang/elixir.git cd elixir...
New
arcanemachine
https://nitter.net/josevalim/status/1744395345872683471 https://twitter.com/josevalim/status/1744395345872683471
New
ricklove
I was just introduced to Elixir and Phoenix. I was told about the 2 million websocket test that was done 2 years ago. From my research, t...
New
marciol
Please, let me know if this kind of discussion already took place in another topic . Hi all, how do you consider if is better to build ...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
ben-pr-p
In general I’ve been sticking to this community style guide GitHub - christopheradams/elixir_style_guide: A community driven style guide ...
New
tomekowal
Hey guys! I want to create a toy project that shows a chart of temperature over time and updates every 5 seconds. I feel LiveView is per...
New
opsb
We’re considering our architecture from a viewpoint of scaling our traffic heavily over the next 6 months. Our current deployment is runn...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
slashdotdash
Phoenix Live View is now publicly available on GitHub. Here’s Chris McCord’s tweet announcing making it public.
New

Other popular topics Top

albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

We're in Beta

About us Mission Statement