我正在尝试用Elixir创建一个周期性的计时器,它将周期(毫秒)的长度作为参数,并在经过时间后执行一个函数。用户也应该有权取消计时器。如果传递的函数返回:cancel
值,那么它也应该取消计时器。下面是我的代码:
def start_timer(period, callback_function) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
timer = Process.send_after(self(), :work, period)
{:ok, %{timer: timer}}
end
def handle_info(:work, period) do
IO.puts("This should printed after every period")
# Start the timer again
timer = Process.send_after(self(), :work, period)
{:noreply, %{timer: timer}}
end
我还没有实现cancel-feature或参数函数的执行,因为上面的方法还不起作用。我该怎么做呢?任何建议都是非常感谢的。
发布于 2021-11-04 08:43:34
看起来你没有在任何地方使用你的callback_function
。如果你想让它在计时器的整个运行过程中都可用,你需要在第二个参数中将它传递给GenServer.start_link/3
,例如
GenServer.start_link(__MODULE__, %{callback_function: callback_function}, name: __MODULE__)
下面是一些定期执行函数的代码:
defmodule Cronlike do
use GenServer
@allowed_units [:second, :minute, :hour, :day]
def start_link(state) do
GenServer.start_link(__MODULE__, state)
end
@impl true
def init(%{interval: interval, unit: unit} = state) when is_integer(interval) and interval > 0 and unit in @allowed_units do
# wait, bang:
Process.send_after(self(), :tick, to_ms(interval, unit))
# bang, wait:
# send(self(), :tick, to_ms(interval, unit))
{:ok, state}
end
@impl true
def handle_info(:tick, %{interval: interval, unit: unit,
callback_function: callback_function} = state) do
case callback_function.() do
:cancel -> send(self(), :quit)
_ -> nil
end
Process.send_after(self(), :tick, to_ms(interval, unit))
{:noreply, state}
end
def handle_info(:quit, state) do
{:stop, :normal, state}
end
defp to_ms(interval, :second), do: interval * 1000
defp to_ms(interval, :minute), do: to_ms(interval, :second) * 60
defp to_ms(interval, :hour), do: to_ms(interval, :minute) * 60
defp to_ms(interval, :day), do: to_ms(interval, :hour) * 24
end
这篇文章改编自我在Wait, Bang vs. Bang, Wait: Subtleties in Elixir Cron Scripts写的一篇相关文章
https://stackoverflow.com/questions/69839878
复制