在调用gen_server:reply(From, Msg) 中,From 不仅仅是客户端:它实际上是一个包含两个值的元组,调用者的进程ID 和唯一引用。我们可以在the implementation of gen_server:reply/2看到这个:
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
reply({To, Tag}, Reply) ->
catch To ! {Tag, Reply}.
这个想法是Tag是调用者提供的一个唯一值,以便调用者可以将这个调用的结果与任何其他传入消息区分开来:
Ref = make_ref(),
MyServer ! {'$gen_call', {self(), Ref}, foo},
receive
{Ref, Reply} -> io:format("Result of foo call: ~p~n", [Reply])
end
在上面的代码中,receive 将阻塞,直到它得到对这个调用的响应。
(gen_server:call/2 执行与上述类似的操作,并额外监控服务器以防崩溃,并检查超时。)
未记录的原因是它被视为内部实现细节可能会发生变化,建议用户依赖 gen_server:call 和 gen_server:reply 而不是自己生成和匹配消息。
大多数时候您根本不需要使用gen_server:reply/2:服务器进程接收到一个调用并同步处理它,返回一个reply 元组:
handle_call(foo, _From, State) ->
%% ignoring 'From' here, because we're replying immediately
{reply, foo_result, State}.
但有时您希望服务器进程延迟响应调用,例如等待网络输入:
handle_call(foo, From, State) ->
send_request(foo),
NewState = State#state{pending_request = From},
{noreply, NewState}.
handle_info({received_response, Response}, State = #state{pending_request = From}) ->
gen_server:reply(From, Response),
NewState = State#state{pending_request = undefined},
{noreply, NewState}.
在上面的示例中,我们将From 值保存在服务器状态中,当响应以 Erlang 消息的形式传入时,我们将其转发给调用者,调用者将一直阻塞直到收到响应。 (一个更现实的例子是同时处理多个请求,并以某种方式将传入的响应与未完成的请求相匹配。)