如何使用Phoenix.Channel.reply / 2对通道推送进行异步回复

时间:2016-05-23 12:51:37

标签: phoenix-framework phoenix-channels

我正在尝试将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回调。回复/ 3将在您需要的极少数情况下使用   在另一个过程中执行工作并在完成时回复   使用socket_ref / 1生成对push的引用。

当我生成并传递一个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似乎只会增加我的代码的开销。

使用广播/ 3 对我的解决方案使用socket_ref和回复/ 2 有什么好处?如何通过回复/ 2获得解决方案工作?

2 个答案:

答案 0 :(得分: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
...

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客户端库每次推送事件最多只能接受一次回复。

答案 1 :(得分:0)

希望我对这次谈话并不太晚。

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

希望有所帮助!