You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Elixir实现支持主动取消与回调自动取消的周期性定时器

Elixir 周期性定时器实现方案

原有代码的核心问题有两点:

  • GenServer启动后的初始化逻辑位置错误:你把Process.send_after的逻辑写在了启动GenServer的父进程中,定时器消息实际发给了父进程而非GenServer进程,状态也没有正确存储周期、回调函数两个核心参数
  • 没有预留取消逻辑、回调执行逻辑的相关实现

完整调整后实现

defmodule PeriodicTimer do
  use GenServer

  # 对外暴露的API
  @doc "启动定时器,参数为周期(毫秒)、回调函数,返回GenServer进程pid用于后续操作"
  def start(period, callback) when is_integer(period) and period > 0 and is_function(callback, 0) do
    GenServer.start_link(__MODULE__, %{period: period, callback: callback})
  end

  @doc "主动取消指定定时器"
  def cancel(timer_pid) do
    GenServer.call(timer_pid, :cancel)
  end

  # GenServer 内部回调
  @impl true
  def init(state) do
    # 初始化时启动第一次定时器
    timer = Process.send_after(self(), :run_callback, state.period)
    {:ok, Map.put(state, :timer_ref, timer)}
  end

  @impl true
  def handle_info(:run_callback, state) do
    # 执行传入的回调函数
    callback_res = state.callback.()

    if callback_res == :cancel do
      # 回调返回:cancel时直接终止进程,自动清理剩余定时器
      {:stop, :normal, state}
    else
      # 启动下一轮定时器
      new_timer = Process.send_after(self(), :run_callback, state.period)
      {:noreply, %{state | timer_ref: new_timer}}
    end
  end

  @impl true
  def handle_call(:cancel, _from, state) do
    # 主动取消时先清理未触发的定时器
    Process.cancel_timer(state.timer_ref)
    {:reply, :ok, state}
  end
end

使用示例

# 示例1:每1秒执行一次,执行5次后自动终止
count = :counters.new(1, [:atomics])
{:ok, timer_pid} = PeriodicTimer.start(1000, fn ->
  current = :counters.get(count, 1)
  IO.puts("第#{current + 1}次执行")
  :counters.add(count, 1, 1)
  if current >= 4, do: :cancel, else: :ok
end)

# 示例2:主动取消定时器
# PeriodicTimer.cancel(timer_pid)

额外说明

  • 上述实现支持同时启动多个独立的定时器,如果你需要全局单实例定时器,可以在GenServer.start_link的参数中添加name: __MODULE__固定进程名
  • 如果需要回调执行结束后再间隔指定周期启动下一轮,可以把Process.send_after的逻辑移到回调执行完成之后再调用,避免回调执行时间过长导致的任务堆积

内容的提问来源于stack exchange,提问作者Mr. Engineer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 23:06:01