I was using handle_info before and had a call to an API. I want to use handle_continue so that if the API is delayed, it doesn’t delay everything else.
Note that if you do a timeout and another message somehow comes in before your timeout, it will trigger before your initialization is complete.
So it seems like using handle_continue is correct (see example below), but I’m still not clear why you have wait after initialization to do the API call. Are you waiting for something in general?
def start_link...
def init(setup) do
... # initialization code
{:ok, initial_state, {:continue, :bootstrap}} #immediate continuation
end
def handle_continue(:bootstrap, state) do
# this happens immediately after the above
perform_api_call()
{:noreply, new_state}
end
got it, so you might want something like this. Note that this one does initial call after 5 minutes (the first api call happens after 5 minutes) but I think you should be able to string together an “immediate first initial call” from my code snippets! Hope this helps!
@polling_frequency 5 * 60 * 1000 # 5 minutes, in milliseconds
def start_link...
def init(setup) do
... # initialization code
schedule_api_call()
{:ok, initial_state}
end
def handle_info(:do_api_call, state) do
perform_api_call()
schedule_api_call()
{:noreply, new_state}
end
defp schedule_api_call(delay \\ @polling_frequency), do: Process.send_after(self(), :do_api_call, delay)
this code shouldn’t block anything (scheduling a message is basically instantaneous), except during the api call itself! Do you expect the api call to be really slow and impinge on other activities of the gen_server? You can set up an async handler for the api call (but this will incur complexity in your code, so I would be careful.)
The idea is that on the chance that the API call is slow; other activities should continue instead of wait for it. I’m not an expert at async inside of Elixir so any advice would be very welcome .
ok. My suggestion would be to try it out first. I would bet that it doesn’t matter. If it becomes a problem, here is how you would do it:
def handle_info(:do_api_call, state) do
this = self()
Task.Supervised.start_child(MyTaskSupervisor, fn ->
report_back_from_api(this, do_api_call())
end)
schedule_api_call()
{:noreply, new_state}
end
defp report_back_from_api(srv, result), do: GenServer.call(srv, {:api_result, result})
You’ll have to set up a Task.Supervisor called MyTaskSupervisor in your MyApp.Application startup, and you’ll also have to implement the handle_call for {:api_result, result}
Like I said, it’s more complicated, but, you know, not terribly hard. I’d rather use this than Python twisted.