如何使用Phoenix.Channel.reply/2异步回复频道推送

How to use Phoenix.Channel.reply/2 for async reply to channel push

我正在尝试将 Phoenix 文档中 Phoenix.Channel.reply/2 的示例扩展为异步回复 Phoenix channel/socket 推送事件:

Taken from 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.reply/2[ 生成和传递 socket_ref =52=]。相反,它依赖于 Phoenix.Channel.broadcast/3.

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

reply(arg1, arg2)

Replies asynchronously to a socket push.

Useful when you need to reply to a push that can’t otherwise be handled using the {:reply, {status, payload}, socket} return from your handle_in callbacks. reply/3 will be used in the rare cases you need to perform work in another process and reply when finished by generating a reference to the push with 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
...

worker.ex

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

我的 room_channels.ex handle_info 函数被调用,但 reply/2 似乎没有通过套接字发送消息。我在 stderr 上没有看到任何堆栈跟踪,也没有在 stdout 上看到任何指示错误的输出。更重要的是,跟踪 socket_ref 似乎只会增加我的代码开销。

使用 socket_ref 和 reply/2broadcast/3[=52 的解决方案有什么好处=] 以及如何使用 reply/2 解决方案?

我错了,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
...

worker.ex

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

在我的实现中,我犯了一个错误,即使用 {:reply, :ok, socket} 的 return 值而不是 {: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 上测试过

希望对您有所帮助!