How do we implement a reset-able countdown timer with a GenServer?
1) perform a task after fixed amount of time, say every 60 seconds
2) have a way to reset the countdown back to 60 seconds before the timer elapses
I have looked at How to perform actions periodically with Erlang's gen_server? but it doesn't quite cover the aspect of resting the timer before the countdown elapses.
Thanks.
How can I schedule code to run every few hours in Elixir or Phoenix framework? defines how to do a periodic job.
To implement a cancel using that as a base you could do the following:
defmodule MyApp.Periodically do
use GenServer
def start_link(_name \\ nil) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def reset_timer() do
GenServer.call(__MODULE__, :reset_timer)
end
def init(state) do
timer = Process.send_after(self(), :work, 60_000)
{:ok, %{timer: timer}}
end
def handle_call(:reset_timer, _from, %{timer: timer}) do
:timer.cancel(timer)
timer = Process.send_after(self(), :work, 60_000)
{:reply, :ok, %{timer: timer}}
end
def handle_info(:work, state) do
# Do the work you desire here
# Start the timer again
timer = Process.send_after(self(), :work,60_000)
{:noreply, %{timer: timer}}
end
# So that unhanded messages don't error
def handle_info(_, state) do
{:ok, state}
end
end
This maintains a reference to the timer, allowing it to be cancelled. Every time the :work
message is received, a new timer is created and stored in the state of the GenServer.