-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEATURE] Removes Chains in Tracked Properties Flag #17951
Conversation
b91752f
to
97b5d28
Compare
8ce9706
to
6cb5921
Compare
33e4200
to
4cdbb8c
Compare
This PR updates the tracked properties feature flag to fully remove chains. This was primarily is response to core issues we found with interop with computed properties, which required us to make computed properties and observers more lazy in general. At a high level, these are the major changes: * The `getCurrentTracker`/`setCurrentTracker` system was a leaky abstraction, since errors could cause a child tracker to never reset. In order to make it more bulletproof, it has been changed to `track`/`consume`/`isTracking`. We wrap any change to the stack of trackers with a `try/catch` to make sure we always clean up in `track`. `isTracking` is used only when we don't want to eagerly create a tag if there is no tracking context. * Observers have been made asynchronous. They now flush during specific phases of the runloop - just before render, and potentially after render if anything was scheduled (this will begin a new runloop). They poll now to check if any changes occured, rather than firing synchronously when the change occurs, which is how we made them work entirely using tags without chains. Observers inherited from EmberObject classes begin polling at the same time as `finalizeChains`. * Computed properties have been updated to only use chains when checking for dirtiness. Since computed properties were lazy before, this isn't much of a change overall. Computed properties also no longer autotrack, unless they have been marked by a (currently private) `_auto()` modifier. * Both observers and computeds accomplish this laziness by following and reading the tags of their dependencies _after calculation_. They entangle all dependencies at this point, _unless_ the dependency is an uncalculated computed property. If they encounter one of these, they setup _lazy chains_, which will be followed and updated the next time the computed property is calculated. This also makes aliases lazily observe. * Computed properties have also been updated to install a native setter, per the track props update RFC. * Query parameters use synchronous observers to update various things. They should really be refactored, but that's going to take a while. In the meantime, we flush the observers synchronously for them specifically. * A `UNKNOWN_PROPERTY_TAG` system has been added _privately and internally only_. This system allows proxies to return a special tag that invalidates when their _content's_ properties change. This system could be made more public in the future, but it is purposefully private for the time being. It was necessary to match existing semantics in many tests. * A new mandatory setter system has been added. This system is now one-way - once a value has been consumed, there is no way to remove the setter and "unconsume" it. This is the nature of tags being lazy and having no teardown. There were a few additional changes that were required as well: * `visit` and `transitionTo` for application tests had to be made async in order to work properly with observers. Most of the work occured in another PR, but some had to be finished up here. * Many, many tests needed to be updated. Most of these were for the fact that observers are now async, and required us to wait on the runloop settling.
4cdbb8c
to
405d423
Compare
Discussed in today's Ember core team meeting, and we should land this as soon as we confirm that when the feature is disabled, there is no performance regression. We can iterate (on master) to fix any performance regressions when the feature is enabled... |
Looks like this is fine with the flag off, so I'm going to go ahead and merge this. |
Might this be a breaking change? https://api.emberjs.com/ember/3.9/classes/Observable/methods/incrementProperty?anchor=set As an example, this change has broken some helpers in ember-composable-helpers: https://github.com/DockYard/ember-composable-helpers/issues/318 |
@GavinJoyce yep, definitely might be, all work was done behind a feature flag so that we can test and experiment the impact |
Thanks, I'm going to try a custom ember-souce build on Intercom to see what breaks. We have ~200 observers in older parts of our app. I'm happy to migrate away from them if necessary, but it would be pretty sad if we couldn't upgrade to Octane first and then incrementally migrate to tracked properties. |
@GavinJoyce FWIW, the only semi-common use case we found where sending observers synchronously was really required was if you were using them to invalidate a computed property, and we have a way to mitigate that using tracked properties. That said, you could definitely see how this could cause issues timing-wise in apps with heavy observer usage, so your feedback would definitely be valuable! Unfortunately, this is the only way we could make observers interop with tracked properties, since they're fundamentally lazy, so if we this is too problematic and we do have to revert it, it means we'll also have to drop that interop 😕 |
@GavinJoyce we just merged an update that brings back synchronous observers, and adds the ability to opt-into async observers. Can you let us know if your apps are able to work with the latest changes? We need testers here to make sure things are solid before we ship, definitely appreciate your input! 😄 |
Thanks @pzuraq, we've just upgraded our app to 3.10 so I'm going to spend some time next week working with 3.11.beta and canary. I'll also upgrade the Octane app that I'm building for a future tutorial and try it with |
I've confirmed that https://github.com/DockYard/ember-composable-helpers/issues/318 is no longer an issue with ember canary. I'll currently addressing issues with our Intercom app in |
This PR updates the tracked properties feature flag to fully remove
chains. This was primarily is response to core issues we found with
interop with computed properties, which required us to make computed
properties and observers more lazy in general.
At a high level, these are the major changes:
getCurrentTracker
/setCurrentTracker
system was a leakyabstraction, since errors could cause a child tracker to never reset.
In order to make it more bulletproof, it has been changed to
track
/consume
/isTracking
. We wrap any change to the stack oftrackers with a
try/catch
to make sure we always clean up intrack
.isTracking
is used only when we don't want to eagerlycreate a tag if there is no tracking context.
phases of the runloop - just before render, and potentially after
render if anything was scheduled (this will begin a new runloop).
They poll now to check if any changes occured, rather than firing
synchronously when the change occurs, which is how we made them work
entirely using tags without chains. Observers inherited from
EmberObject classes begin polling at the same time as
finalizeChains
.checking for dirtiness. Since computed properties were lazy before,
this isn't much of a change overall. Computed properties also no
longer autotrack, unless they have been marked by a (currently
private)
_auto()
modifier.reading the tags of their dependencies after calculation. They
entangle all dependencies at this point, unless the dependency is an
uncalculated computed property. If they encounter one of these, they
setup lazy chains, which will be followed and updated the next time
the computed property is calculated. This also makes aliases lazily
observe.
per the track props update RFC.
They should really be refactored, but that's going to take a while.
In the meantime, we flush the observers synchronously for them
specifically.
UNKNOWN_PROPERTY_TAG
system has been added privately andinternally only. This system allows proxies to return a special tag
that invalidates when their content's properties change. This system
could be made more public in the future, but it is purposefully
private for the time being. It was necessary to match existing
semantics in many tests.
one-way - once a value has been consumed, there is no way to remove
the setter and "unconsume" it. This is the nature of tags being lazy
and having no teardown.
There were a few additional changes that were required as well:
visit
andtransitionTo
for application tests had to be madeasync in order to work properly with observers. Most of the work
occured in another PR, but some had to be finished up here.
that observers are now async, and required us to wait on the runloop
settling.
TODO: