Socket and channels lifecycle in a single page application


I’m building a game where n players play a party (don’t know the actual english word, like a game or a match) together. It’s kind of a quiz game.

This is a single page application, where a user can create a new party and send an invitation link to other players. Players who play several parties in a row never leave or reload the page, I use a router component with Svelte (it’s like Vue or React).

When a party is created, players join a party:XXXXXXX channel. The channel pushes the game state on every change, and receives players answers for the quiz questions.

I’ve set up a bunch of listeners on the channel, with channel.on('some_event', fn), also an error reporting system with channel.onError, etc.

When the party is finished, I call channel.leave(), and when a new party is started, I rebind my channel variable to the new channel.

But I have the feeling it is not enough, I have no guarantee that all these event listeners are properly cleaned up, and that their are not hanging somewhere with a lot of closures. Should I call…) before leaving the channel, for all listeners previously set up ? Would it be enough ?

Or should I instead join a private channel for the player, where I subscribe to parties server side ? I use the join/3 function (elixir side) to verify that the player has been invited to the party (or created it), or reply :error otherwise, and I can keep the party ID in the socket data, so having a dedicated party channel is straightforward. But on the otherside it feels complicated to handle join rejections and managing channel setup/cleanup, where having a player channel would there be the straightforward solution.

Does someone have a similar situation or any advice on this topic ?

Thank you

I think the current way of handling things like you described should be perfectly fine. I think the only way to validate your concerns (to not have a memory leak) is to actually validate it with profiling of the browsers memory which you can do in for example chrome devtools in the following way:

My reasoning behind it is the following; if you look at the source of phoenix.js (the client side channel impl) then you see that all closures you give to the on() function on a channel are stored inside a channel object inside the bindings variable (array). When you throw away the channel variable (which you do because it represents a single game) then it should be garbage collected together with all the bindings(closures) that are stored inside as the GC can infer that nobody is using it anymore. (see here: )

But again you could validate this with the devtools yourself if you have doubts (could be quite interesting to do :slight_smile: )

Have fun while coding the game!

1 Like

Thanks for the pointer. I’ll keep it that way for now and run some measurements when the codebase is stable.