Open ChristophP opened 7 years ago
Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!
Here is what to expect next, and if anyone wants to comment, keep these things in mind.
I actually found out that leaking stuff is not necessarily specific to multiple elm apps on the page, but more to reinitializing elm modules(calling Elm.<Module>.embed/fullscreen()
). This can happen for example when using react-elm-components
and mounting the component multiple times (i.e. by navigating to a part of the page where the component isn't shown and then back to where it is) if subscriptions are used without reloading the page.
I created an example here: https://ellie-app.com/3LHDqnYqD4za1/10
@ChristophP Almost a year later, but I just came up with a workaround for this issue; using web components, you can fire a custom event when the elm app is detached from the DOM. You could then create a custom program function that automatically disconnects subscriptions when the app is unmounted. I have not tested this extensively yet, but I can verify it fixes the ellie example you posted above. Here's the ellie:
Adding workaround for the memory leak where smaller Elm apps embedded in other things - https://github.com/Pilatch/angularjs-ng-elm#technical-stuff
@joefiorini I haven't thought of using web components callbacks yet but nice idea. I have been using something similar with ports and callbacks to clean up lingering subscriptions. But it would we cool if there was a function for elm that clobbers everything down on command. However now with 0.19 out, ther only seems to be a strong use case for this with Browser.element
. The other ones will pretty much always live as long as the page does.
+1 Elm applications are a great candidate for stable drop-in components, and having no way to cleanup lingering listeners is very detrimental.
Is this still a thing in 0.19?
@FranzSkuffka Yup
Here's another use case:
I'm using Elm in a browser extension content script, so I inject a little Elm app on certain pages. This is happening on someone else's single-page-app, so when the user navigates away from the current view, I need to clean up my Elm stuff.
Up! I have a very similar situation as @ChristophP have.
I second the need/want for a way to completely stop, unsubscribe, ..., cleanup the Elm app on demand. My usecase is similar - SPA composed of various sub-apps (Elm, React, etc.).
I would suggest this being called terminate()
instead of kill()
. This mimics the Worker API and sounds less violent.
I just came up with another reason why a stop, unsubscribe, destroy mechanism for an elm app/element would be good:
the debugger
The debugger records a history of all events that happened. In a scenario with multiple pages and Browser.element
s running on different pages each one will have a debugger instance.Now when on pageA a debugger will be spawned, when navigating to page B a new debugger will be spawned. When navigating back to page A, yet another debugger for page A we be created and the old one is still in memory. Depending on the frequency is size of messages this leaks memory over time in development.
So all in all what a app.kill/app.destroy
should do to accomodate the scenarios for mulitple Browser.element
s on the page:
Noting here a reference implementation by @supermario https://gist.github.com/supermario/4c2615806c6c561a16edf5dd7208a759#js-modification-solution
I just opened #1125 and I used the name shutdown
(the name of the commit is still incorrect). I was looking for nicer words than "destroy", "terminate", "kill".
I considered:
Very interesting. And thanks for sharing the gist.
The problem
We have an app shell architecture, meaning a host app that loads several other apps on our page. This lets us maintain and deploy each app separately. The app shell routes to
/app1
and App 1 will route to everything after/app1/...
. Some of the loaded apps are usingNavigation.program
. When the path changes from/app1
to/app2
, App 1 still listens to popstate events even though it should be idle.Possible workarounds
There are a couple of ways to circumvent this from being a problem.
Removing all subscriptions. => Won't work. There is no way to tell the
Navigation.program
to stop listening to this event since the subscription is untouchable and always there https://github.com/elm-lang/navigation/blob/master/src/Navigation.elm#L73."Turning off" the App1 when routing away (with something like this
update = (\model msg -> if model.isMounted then update model msg else (model, Cmd.none))
) => would work but bloats the model and update function a little bit.Making sure each app only parses routes that are within it's namespace (only parse stuff starting with
/app1
)All these methods will just elimate the reaction to the event, rather than removing the actual event listener.