dantswain
Hi! I was wondering what patterns people have used for the following scenario.
- When the page first loads, spawn some (potentially longer-running, e.g., fetching data from an external API) task.
- When that task finishes, update the UI.
What I have after a couple iterations is something like this:
# do this in handle_params because I want live_patch to trigger it
def handle_params(params, _uri, socket) do
if connected?(socket) do
view_pid = self()
spawn(fn ->
result = make_slow_api_call(params)
send(view_pid, {:api_call_done, result})
end)
end
{:noreply, socket}
end
def handle_info({:api_call_done, result}, socket) do
{:noreply, assign(socket, api_result: result)}
end
defp make_slow_api_call(...) do
# ...
end
This works OK, however there are a couple shortcomings that I would love to handle better:
- It’s hard to test for multiple reasons.
make_slow_api_callis private, so the only way to trigger it is to render the view.- Whenever I render the page, I need to wait for
make_slow_api_callto finish, else I have stray processes after the test finishes. Doing this is not trivial and is error prone.
- Handling errors is complicated - I could
spawn_linkbut if there’s any error in the task then the view reloads, which is ok sometimes but not if the error is due to, say, an upstream service being down, which would lead to an infinite loop of reloading. I can monitor the process, it just feels clunky to need to do that on my own.
I’m curious if anyone has come across a better pattern for this, or maybe there’s something obvious I have just missed.
Trending in Questions
Hello!
Suppose you are building workflow (order / task / payment) processing system with the following requirements:
Each workflow con...
New
Hey guys,
I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly
Do you guys have any suggestions what is the best prac...
New
Kia ora,
We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
Hello!
Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app.
I creat...
New
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
Hello,
I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter).
The diffic...
New
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
Other Trending Topics
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
There are three potential reasons for members of this forum to have a look at https://vutuv.de
You are tired or annoyed of LinkedIn.
Yo...
New
ICal is a library for interacting with iCalendar data. It parses iCalendars into typed Elixir structs via ICal.from_ics, and can prepare ...
New
Latest Phoenix Threads
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #ai
- #phoenix_html
- #elixirconf-us
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










First Post!- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
derek-zhou
For long running tasks that are entirely under my control, I will do the same thing. However, for external API calls that usually has some sort of rate limiting, I will just use a GenServer for each API endpoint. If you really need parallel access then you can use a NimblePool.
Most Liked
benwilson512
I think the thing here is less about managing access to the remote server and more about how to not block your liveview processes whether you’re directly pinging the remote endpoint or whether it goes through a genserver.
@dantswain this is a good question! We’ve done basically the same thing (I think we use a task but same idea) and it definitely is a challenge for testing. I think our solution involved setting an assign which controlled whether it was done sync or async, and then in the tests we set that assign to sync. Definitely an area that could use some slightly more ergonomic patterns though!
dantswain
Thanks for the good suggestion @vsoraki. We generally do use a similar pattern to that to mock out our api calls. The thing that makes this difficult to test is that the call (mocked or not) is being done in a process that is spawned from the view. So to test it, we need to know when that separate process has finished, sent a message back to the view process, and the view process has consumed that message.
The simplest way to do that is to repeatedly render the view and check for the effect that you expect, but that can be slow and fail in unexpected ways. What we’ve been doing is actually using the API mock (using Tesla) to capture the pid of the spawned process and relay it back to the test process, then have the test process monitor the spawned process and
assert_receivethat we get the:DOWNmessage for the spawned pid. This works ok most of the time but is still sometimes flakey (false negatives) and it requires a bit of overhead in terms of set up.It looks something like this
Supamic
I just wanted to post this shorter ElixirConf talk and accompanying github repo this for anyone else landing here looking for more recent information on this topic because I found it very useful.
https://github.com/chrisgreg/async_tasks