Is there a way to prevent socket from being closed onbeforeload with phoenix channels?

Hi I’m new to phoenix so I might be missing something obvious.

I have a channel with a potentially long running process linked to it. If the process is running and the users try to reload the page, I would like to give them a chance to bail and stay on the page without killing the process.

However, when the socket is closed, I need to kill the process.

Unfortunately, in the Socket class an eventlistener that closes the connection is added to the “beforeunload” event, rather than the “unload” event. So the connection is closed (and the process killed) before the users get decide whether to wait a bit longer.

Is there a way to prevent this behavior and is there a specific reason the eventlistener is added to “beforeunload” rather than “unload” ?
Will I break my app if I change this in the phoenix.js file?

Thanks in advance for any help/tip.

the reason is handling “backgrounded” tabs and fast recovery from backgrounded/stalled state… https://github.com/phoenixframework/phoenix/pull/3327/commits/1c9589f6bf0eef215b1a5d8cd06345740f298bd7

not sure if changing it to the unload event does the same… and if you override the beforeunload you might see issues with “backgrounded” tabs…

I don’t believe unload is fired in some versions of safari/mobile safari. We need to fire this because of the back/forward cache in browsers restores the page and js state. So phoenix.js will at any time find itself in a disconnected state because of this, which the onbeforeunload disconnect + aggressive reconnect allows us to handle. Note that Safari and others don’t fire the pageshow event, so this approach is necessary.

I would gladly accept a PR to control this behavior, but I’m wondering if you have tried to add your own window onbeforeunload listener and alerting the user there? As long as you bind it before you instantiate the socket, it should get precedence and allow the alert dialog to cancel the bubbled event. In this case, no API change is necessary. Can you confirm?

1 Like

Thank you for the explanations.
Unfortunately, even if I bind it before instantiating the socket the abnormalClose(“unloaded”) gets triggered, which means that while the user can stay on the page, the terminate/2 callback gets triggered on my channel where I kill the long running process, which is what I’m trying to avoid in this case.
For now, if the users leave the page while the process is running then I’ll kill it. After all, I have a very visible spinner that shows something is happening.
Thanks.

You could use e.g. turbolinks or some other “single-page” solution to prevent the websocket disconnection. While I’m not sure how it works I know some pages can alert if you have e.g. unsaved content in the page and let you choose to leave or stop. Maybe that might be an option as well.

what did you call to stop the event bubbling up to the phoenix.js handler? (e.stopImmediatePropagation(); or something else ? )

Silly me! I forgot to stop the propagation ! Now it does work exactly the way I need it to and the phoenix.js handler is not triggered if the user decides not to leave the page.
Thank you

1 Like

Awesome! Glad you got things sorted. If you’d like to write up a js doc PR for handling unload, I’d appreciate it.

I will give it a try. Thanks again

I use unpoly.js for all that, it works fantastically. ^.^