实现GenServer handle_call/3可以返回:continue以调用附加函数。是否可以保证此函数相对于其他消息的运行时间?
例如,考虑这个仅保留运行计数器的模块:
defmodule Tmp do
use GenServer
def start_link(opts), do: GenServer.start_link(__MODULE__, 0, opts)
def incr(tmp), do: GenServer.call(tmp, :incr)
@impl true
def init(state), do: {:ok, state}
@impl true
def handle_call(:incr, _from, n) do
{:reply, n, n, {:continue, :incr}}
end
@impl true
def handle_continue(:incr, n) do
{:noreply, n+1}
end
end
Run Code Online (Sandbox Code Playgroud)
当您调用 时Tmp.incr/1,该handle_call/3方法返回计数器的当前值,但随后也返回:continue。这会导致GenServer基础设施调用handle_continue/2。
如果我Tmp.incr/1连续调用两次,我能保证得到递增的值吗?或者是否有可能在被调用handle_call/3之前被调用两次?handle_continue/2
iex> {:ok, tmp} = Tmp.start_link([])
iex> Tmp.incr(tmp)
0
iex> Tmp.incr(tmp)
1
# If I type fast enough, will this ever return 0?
Run Code Online (Sandbox Code Playgroud)
是的,:continue是同步的 - continue 将在您返回它的函数之后立即执行。它的用例正是这样的:保证它将在之后立即运行,这是其他方法无法保证的,例如:timeout或使用 向自己发送消息send(self(), :incr)。
GenServer 回调的文档中简要提到了这一点:
返回 {:reply,reply,new_state,{:continue, continue}} 与 {:reply,reply,new_state} 类似,除了将在之后立即调用 handle_continue/2,并将值 continue 作为第一个参数。
另外在超时部分:
由于消息可能在设置超时之前到达,因此即使超时为 0 毫秒也不能保证执行。要立即无条件地执行另一个操作,请使用 :Continue 指令。