【问题标题】:How to use Phoenix.Channel.reply/2 for async reply to channel push如何使用 Phoenix.Channel.reply/2 异步回复频道推送
【发布时间】:2017-02-25 23:21:18
【问题描述】:

我正在尝试将 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
...

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。相反,它依赖于 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
...

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 似乎只会给我的代码增加开销。

与我的 broadcast/3 解决方案相比,使用 socket_ref 和 reply/2 有什么好处,以及如何通过 reply/2 获得解决方案上班?

【问题讨论】:

    标签: phoenix-framework phoenix-channels


    【解决方案1】:

    我错了,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}而不是{:noreply, socket}的返回值向事件推送发送同步回复。

    在仔细检查从服务器发送到客户端的 websocket 帧后,我发现浏览器确实收到了来自 reply ref, {:ok, result} 的服务器回复,但从未调用过相关的回调。

    Phoenix 的 Socket.js 客户端库似乎每个推送事件最多接受一个回复。

    【讨论】:

      【解决方案2】:

      希望这次谈话不会太晚。

      我设法使用上面的代码并让回调在 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 上测试过

      希望有帮助!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-12
        • 1970-01-01
        • 2018-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-25
        相关资源
        最近更新 更多