0

我正在尝试将 Phoenix 文档中的Phoenix.Channel.reply/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
...

工人.ex

...
receive do
  {pid, job} ->
    result = perform(job) # stub
    send pid, {:work_complete, result}
end
...

该解决方案有效,但它不依赖于socket_ref(socket)Phoenix.Channel.reply/2上生成和传递 socket_ref 。相反,它依赖于Phoenix.Channel.broadcast/3

该文档暗示,reply/2专门用于这种异步回复套接字推送事件的场景:

回复(arg1,arg2)

异步回复套接字推送。

当您需要回复无法使用 {:reply, {status, payload}, socket} 从您的 handle_in 回调返回的推送时很有用。reply/3 将用于极少数情况下,您需要在另一个进程中执行工作,并在完成时通过使用 socket_ref/1 生成对推送的引用来回复。

当我生成并传递一个 socket_ref,并依靠Phoenix.Channel.reply/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
...

工人.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的解决方案正常工作?

4

2 回答 2

0

希望我这次谈话不会太晚。

我设法使用上面的代码并让回调在 javascript 中工作。

诀窍是听phx_reply事件。在priv/static/app.js每个Channeljavascript 对象内部都有一个预定义的 in CHANNEL_EVENTS(line) 列表,并设置为在以下代码块中监听它:

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 上进行了测试

希望有帮助!

于 2017-02-25T15:21:18.203 回答
0

我错了,Phoenix.Channel.reply/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
...

工人.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 客户端库似乎每个推送事件最多接受一个回复。

于 2016-05-24T08:42:24.803 回答