Skip to content

Instantly share code, notes, and snippets.

@sb8244
Created November 14, 2017 20:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sb8244/e6884ad08d91de8c4aa5bf32418557e1 to your computer and use it in GitHub Desktop.
Save sb8244/e6884ad08d91de8c4aa5bf32418557e1 to your computer and use it in GitHub Desktop.
Debounced Phoenix Channel
defmodule Demo.ApiChannel do
require Logger
use Phoenix.Channel
intercept ["api:show"]
# External broadcast interface
def broadcast(:show, tenant_id) do
Demo.Endpoint.broadcast!("api:t" <> to_string(tenant_id), "api:show", %{})
:ok
end
# Internal channel
def join("api:t" <> ch_tenant_id, _params, socket = %{assigns: %{tenant_id: tenant_id}}) do
case to_string(ch_tenant_id) == to_string(tenant_id) do
true -> {:ok, socket}
false -> {:error, %{}}
end
end
def join("api:t" <> _, _, _), do: {:error, %{}}
def handle_in("show", %{}, socket = %{assigns: %{tenant_id: tenant_id}}) do
{:reply, {:ok, api_response(tenant_id)}, socket}
end
# HN: State machine isn't documented here, but the states are idle, debouncing, called
def handle_out("api:show", _, socket = %{assigns: %{demo_show_debounce_state: :idle}}) do
{:noreply, handle_demo_show_call(socket)}
end
def handle_out("api:show", _, socket = %{assigns: %{demo_show_debounce_state: :debouncing}}) do
{:noreply, assign(socket, :demo_show_debounce_state, :called)}
end
def handle_out("api:show", _, socket = %{assigns: %{demo_show_debounce_state: :called}}) do
{:noreply, socket}
end
def handle_info("debounced_api:show", socket = %{assigns: %{demo_show_debounce_state: :debouncing}}) do
{:noreply, assign(socket, :demo_show_debounce_state, :idle)}
end
def handle_info("debounced_api:show", socket = %{assigns: %{demo_show_debounce_state: :called}}) do
{:noreply, handle_demo_show_call(socket)}
end
# HN: pushes to the socket and resets internal state machine information
defp handle_demo_show_call(socket = %{assigns: %{tenant_id: tenant_id}}) do
push socket, "show", api_response(tenant_id)
timer = Process.send_after(self(), "debounced_api:show", 3000)
socket
|> assign(:demo_show_debounce_state, :debouncing)
|> assign(:demo_show_debounce_timer, timer)
end
defp api_response(tenant_id) do
%{api_response: ["just", "demo", "response"]}
end
end
@sb8244
Copy link
Author

sb8244 commented Nov 14, 2017

The state machine isn't documented here, but the goal is to send the initial response to the socket, then send the response every 3s after that. Notice that we can even do something like assign demo_show_debounce_timer a value, because the socket can contain complex data types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment