我正试图将Phoenix文档中Phoenix.Channel.appy/2的示例扩展为异步回复Phoenix通道/套接字推送事件的一个完全可用的示例:
取自https://hexdocs.pm/phoenix/Phoenix.Channel.html#reply/2:
def handle_in("work", payload, socket) do Worker.perform(payload, socket_ref(socket)) {:noreply, socket} end def handle_info({:work_complete, result, ref}, socket) do reply ref, {:ok, result} {:noreply, socket} end
我对示例进行了如下修改:
room_channels.ex
...
def handle_in("work", job, socket) do
send worker_pid, {self, job}
{:noreply, socket}
end
def handle_info({:work_complete, result}, socket) do
broadcast socket, "work_complete", %{result: result}
{:noreply, socket}
end
...
worker.ex
...
receive do
{pid, job} ->
result = perform(job) # stub
send pid, {:work_complete, result}
end
...
此解决方案有效,但它不依赖于使用socket_ref(socket)
和Phoenix.Channel.appy/2生成并传递socket_ref。相反,它依赖于菲尼克斯.Channel.broach/3.
文档暗示reply/2专门用于异步回复套接字推送事件的场景:
回复(arg1,arg2)
异步回复套接字推送。
当你需要回复推送时很有用使用您的handle_in回调。回复/3将用于您需要的极少数情况在另一个进程中执行工作并在完成时答复生成对具有socket_ ref/1的推送的引用。
当我生成并传递socket_ref,并依赖Phoenix.Channel.respoy/2对套接字推送进行异步回复时,我根本无法使其工作:
room_channels.ex
...
def handle_in("work", job, socket) do
send worker_pid, {self, job, socket_ref(socket)}
{:noreply, socket}
end
def handle_info({:work_complete, result, ref}, socket) do
reply ref, {:ok, result}
{:noreply, socket}
end
...
worker.ex
...
receive do
{pid, job, ref} ->
result = perform(job) # stub
send pid, {:work_complete, result, ref}
end
...
调用了我的room_channels.exhandle_info
函数,但reply/2似乎没有通过套接字发送消息。我在stderr上没有看到堆栈争用,也没有在stdout上看到任何指示错误的输出。更重要的是,跟踪socket_ref似乎只会给我的代码增加开销。
与我的广播/3解决方案相比,使用socket_ref和回复/2有什么好处?我如何使用答复/2
我错了,Phoenix.Channel.respoy/2的例子会起作用:
room_channels.ex
...
def handle_in("work", job, socket) do
send worker_pid, {self, job, socket_ref(socket)}
{:noreply, socket}
end
def handle_info({:work_complete, result, ref}, socket) do
reply ref, {:ok, result}
{:noreply, socket}
end
...
worker.ex
...
receive do
{pid, job, ref} ->
result = perform(job) # stub
send pid, {:work_complete, result, ref}
end
...
在我的实现中,我犯了一个错误,用返回值{:reply, :ok, socket}
而不是{:noreply, socket}
向事件推送发送同步回复。
在仔细检查从服务器发送到客户端的websocket帧时,我发现浏览器确实收到了来自reply ref, {:ok, result}
的服务器回复,但从未调用过相关的回调。
Phoenix的Socket.js客户端库似乎每次推送事件最多接受一个回复。
希望我这次谈话不会太迟。
我设法使用了上面的代码,并获得了在javascript中工作的回调。
诀窍是监听phx_reply
事件。在priv/static/app.js
中,每个Channel
javascript对象都有一个在CHANNEL_EVENTS
(行)中预定义的列表,并设置为在以下代码块中侦听:
this.on(CHANNEL_EVENTS.reply, function (payload, ref) {
_this2.trigger(_this2.replyEventName(ref), payload);
});
我所做的是在通道on
回调中,我监听phx_reply
事件:
channel.on("phx_reply", (data) => {
console.log("DATA ", data);
// Process the data
}
这已经在Elixir 1.3和Phoenix 1.2 上进行了测试
希望能有所帮助!