GenServer continuous background job and ability to retrieve its state

I was going to ask:

I don’t understand why you have to run() when getting the state.

But you’re using the @timedelay as a ticking clock.

Just use a timer instead.

def init([]) do
    :random.seed(:os.timestamp)
    time = :random.uniform
    voltage = :math.sin(2 * :math.pi + time)
    timer_ref = Process.send_after(self(), :tick, @timedelay)
    state = %{time: time, voltage: voltage, timer: timer_ref}
    {:ok, state}
  end

  def handle_cast(:run, state) do
    # clear timer or if sent let :tick be handled by handle_info
    if Process.clear_timer(state.timer) do
      new_state = run(state)
      timer_ref = Process.send_after(self(), :tick, @timedelay)
      {:noreply, %{new_state | timer: timer_ref}}
    else
      {:noreply, state}
    end
  end

  def handle_info(:tick, state) do
    new_state = run(state)  # <------------ ALWAYS HAS TO RUN IT
    timer_ref = Process.send_after(self(), :tick, @timedelay)
    {:noreply, %{new_state | timer: timer_ref}}
  end

  def handle_call({:get_state, pid}, _from, state) do
    IO.puts "getting state"
    return = Map.take(state, [:time, :voltage])
    {:reply, return, state}
  end

  defp run(state) do
    new_time = state.time + :random.uniform/12
    new_voltage = :math.sin(2 * :math.pi + new_time)
    new_state = %{state | time: new_time, voltage: new_voltage}
    IO.puts "VALUES #{inspect self()} t/v #{new_time}/#{new_voltage}"
    new_state
  end
end

At least, if I correctly understand what you are trying to do. If not, tell me what I missed.

1 Like