Create a gist now

Instantly share code, notes, and snippets.

A ticker for elixir, which emits a tick event for every interval
defmodule Ticker do
require Logger
# public api
def start(recipient_pid, tick_interval, duration \\ :infinity) do
# Process.monitor(pid) # what to do if the process is dead before this?
# start a process whose only responsibility is to wait for the interval
ticker_pid = spawn(__MODULE__, :loop, [recipient_pid, tick_interval, 0])
# and send a tick to the recipient pid and loop back
send(ticker_pid, :send_tick)
schedule_terminate(ticker_pid, duration)
# returns the pid of the ticker, which can be used to stop the ticker
ticker_pid
end
def stop(ticker_pid) do
send(ticker_pid, :terminate)
end
# internal api
def loop(recipient_pid, tick_interval, current_index) do
receive do
:send_tick ->
send(recipient_pid, {:tick, current_index}) # send the tick event
Process.send_after(self, :send_tick, tick_interval) # schedule a self event after interval
loop(recipient_pid, tick_interval, current_index + 1)
:terminate ->
:ok # terminating
# NOTE: we could also optionally wire it up to send a last_tick event when it terminates
send(recipient_pid, {:last_tick, current_index})
oops ->
Logger.error("received unexepcted message #{inspect oops}")
loop(recipient_pid, tick_interval, current_index + 1)
end
end
defp schedule_terminate(_pid, :infinity), do: :ok
defp schedule_terminate(ticker_pid, duration), do: Process.send_after(ticker_pid, :terminate, duration)
end
defmodule Listener do
def start do
Ticker.start self, 500, 2000 # will send approximately 4 messages
end
def run do
receive do
{:tick, _index} = message ->
IO.inspect(message)
run
{:last_tick, _index} = message ->
IO.inspect(message)
:ok
end
end
end
Listener.start
Listener.run
# will output
# => {:tick, 0}
# => {:tick, 1}
# => {:tick, 2}
# => {:tick, 3}
# => {:last_tick, 4}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment