Great question! I had an answer originally drafted, but in gathering the steps I realized that my mental model was slightly off.
Time for a code safari !
// assume obj here is _something_ :raised_hand: :wave:
Ember.set(obj, 'foo', 'bar');
When Ember.set
runs, the following path is taken:
Ember.set
checks iffoo
were a computed property onobj
(assume for now that it isn’t) – hereEmber.set
checks ifobj.setUnknownProperty
exists (assume that it isn’t) – hereEmber.set
checks iffoo
is a dependent key in an observer / computed or is otherwise “watched” (e.g. due to template usage) (assume that it isn’t) – hereEmber.set
doesobj.foo = 'bar'
– here- If the value is different that the previous value,
Ember.set
callsnotifyPropertyChange(obj, 'foo')
– here notifyPropertyChange
checks if the property is being watched, and notifies any observers or computed properties thatfoo
has changed – herenotifyPropertyChange
then “marks the object as dirty” – heremarkObjectAsDirty
grabs the underlying “reference” / “tag” for that object and property then marks it as dirty – here- If there was a tag,
markObjectAsDirty
callsensureRunLoop
to ensure a runloop is scheduled – here ensureRunLoop
checks if anything has been rendered, and if it has schedules a run loop
Footnotes:
markObjectAsDirty
is an internal utility function primarily used with Glimmer 2’s reference system
tldr; The basic decision tree for when an Ember.set
would schedule work to happen async is:
- Has anything been rendered yet? If not, then
Ember.set
currently will not schedule any async. - Is the new value being set different than the old value? If not, then nothing is notified or scheduled.
- Has the object (if an
ObjectProxy
) or property (for normal objects) been rendered in a template? If not, nothing is scheduled.
If the answers to all of those questions are “yes”, then async will be scheduled…
Phew, sorry about that (it was fun though, huh?)