Polling weather API using GenServer

What would be the best approach to poll a weather API? I want to continually call the function get_weather_from_locations() which returns an array of locations and their weather (high / low). Essentially, I want to display to user the latest weather for each location and update every minute or so.

I went through this tutorial (https://elixir-lang.org/getting-started/mix-otp/genserver.html) but still don’t understand how this would work in the context of the app I want to create. Pointers in the right direction would be appreciated.

This is what I have so far.

  def init(:ok) do
    ny_weather = %{}
    la_weather = %{}
    {:ok, {ny_weather, la_weather}}
  end

  def handle_call({:poll, name}, _from, state) do
    {ny_weather, la_weather} = state
    # TODO: call API and push the new weather result onto ny_weather and la_weather respectively

    {:reply, state}
  end

You can use Process.send_after/4 to schedule a message to the GenServer after a given number of milliseconds. The message will be received in a handle_info callback, where you can reschedule the next poll:

def init(:ok) do
  ny_weather = %{}
  la_weather = %{}
  send(self(), :poll) # Immediately schedule the first poll
  {:ok, {ny_weather, la_weather}}
end

def handle_info(:poll, state) do
  # TODO: perform the call and update the state

  # Re-schedule the next poll in 1 minute (60_000 ms):
  Process.send_after(self, :poll, 60_000)

  {:noreply, state}
end
1 Like

Use info to schedule polling, in handle_info/2 you can cache the weather result in state.
Use call to get the cached weather result, in handle_call/3 return weather result in state.

When I do this I get a bad return value.

iex(4)> Weather.call(pid)
** (EXIT from #PID<0.1073.0>) shell process exited with reason: bad return value: {:reply, %{nyc: "40", la: "70"}}

It says it’s a bad return value but that’s exactly the value I wanted.

This is what my handle_call function looks like.

  def handle_call(:get_state, _from, state) do
    {:reply, state}
  end

Reply always requires a 3-tuple, with the actual value to reply with in the 2nd field and the new state in the 3rd.

handle_call returns a tuple, when you are sending back a response, the tuple should be {:reply, whatever_value_you_want, state} , the first item :reply means you want to send a response back to the caller, the second item is the actual response, and the third item is the new state of your GenServer. If you don’t want to respond, then return a tuple {:noreply, state}.