4 ms·
"z = bind -> x.get() + y.get()" seems nice, but when are the attached listeners removed again? Typically this is solved with weak listeners, but Javascript doe
by madzanderzen 13y ago
"z = bind -> x.get() + y.get()" seems nice, but when are the attached listeners removed again?
Typically this is solved with weak listeners, but Javascript doesn't support weak references. I'm worried that this library never removes the listeners, effectively leaking like crazy?
- xyzzyrz 13y agoAstute observation - this is actually something we do call out in http://yang.github.io/reactive-coffee/infelicities.html#garbage-collection http://yang.github.io/reactive-coffee/infelicities.html#garb.... As you note, JS doesn't have it as easy as in other platforms, but it's not a fundamental problem and is still reasonably straightforward to address (as described). The fix is slated for this coming release, and we'll need to more thoroughly document/explain what this all means.
- madzanderzen 13y agoThanks for the explanation in the link. The problem that is described in the link is clearly solvable, however what I'm referring to is more fundamental. I'm not talking about nested binds, but just a simple bind such as "z = bind -> x.get() + y.get()". This will add listeners to x and y. When will those listeners be removed again (without adding new ones during the recalculation of z's new value in case of a change to x or y)? It may be that x and y are the basic models in my app, and during my clicking around in the application I create and close many panels, and each panel has bind-operations that add listeners to x and y. Now the panels may be long gone, and will never be used again, but when will the listeners that was added to x and y as part of creating the panels be removed?
- xyzzyrz 13y agoYeah, this is what I'm talking about - a fuller explanation would require more text. :) The question is what you're doing with z. As long as you're also adding and removing your hypothetical panels in turn with bind, you're good: div {class: 'container'}, bind -> [ if show.get() div {class: 'z'}, bind -> x.get() + y.get() else div {class: 'nothing'} ] The question is what happens at the top level or when you want to break out and do your own thing. We don't have the luxury of weak refs in JS, and as a result it's less forgiving if you do "silly" things like create binds that go nowhere and that you don't want to keep. But even with weak references, one can't tell if you added a bind only to subscribe a console.log caller to its changes, save to localStorage, or some other side effect that you do want to keep. In any case, it's incumbent on us to fully document what "silly" means, include simple-to-use API calls to capture and dispose entire subgraphs (for when you want to do your own thing manually), and provide good debugging introspection tools for finding these `bind`s to nowhere, in case you really don't want them lingering around. We've also been bouncing around ideas for reversing the reference DAG and having cells named by the reversed paths through the DAG, which we may experiment with. You then get to deal with the converse problem of manually needing to hold references to the sinks in the DAG. In any case, we're very open to learning from how things play out in practice, and shape the direction of the library accordingly.
- madzanderzen 13y agoAhh, you're putting the entire UI in binds. That's a really clever way to handle the lifecycle of the listeners! bind is "stupid" in the way that it completely recalculates a cells value on any change of a cell it's previous calculation depended on. It doesn't "understand" the calculation in a way to only recalculate the part that was strictly needed to update its value to a change. I mention this because if the entire UI is in a bind, then on every change the entire UI would be recreated. I worry that this could get prohibitively slow on a sufficiently complex UI? (It can also lead to problems with widgets losing focus, but that can be solved in a similar way as in Immediate Mode GUI's).
- xyzzyrz 13y agoA bind only re-evaluates if changes happen within its own immediate scope, and not if the changes are within any descendant binds' scopes. Whenever you want to carve off finer-granularity updates, scope things to a separate bind. This has scaled to, for instance, a complex WYSIWYG web page editor, where the whole page/document being edited is structured using cells and rendered with recursive binds - the app needs to be very responsive to each change a user makes, whether it's typing in text or dragging a style slider, even if the focus is on the top-level DOM element. That's not to say we've seen all the use cases - we definitely want to see where things fall down as well.
- madzanderzen 13y agoNice! Thanks for taking the time to explain this :-)
- xyzzyrz 13y agoThese are great questions! The topics deserve attention from the docs, esp. regarding suggested ways of doing things (e.g. the `shown` example from earlier vs. imperatively adding/removing elements).
- madzanderzen 13y ago