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.setchecks iffoowere a computed property onobj(assume for now that it isn’t) – hereEmber.setchecks ifobj.setUnknownPropertyexists (assume that it isn’t) – hereEmber.setchecks iffoois a dependent key in an observer / computed or is otherwise “watched” (e.g. due to template usage) (assume that it isn’t) – hereEmber.setdoesobj.foo = 'bar'– here- If the value is different that the previous value,
Ember.setcallsnotifyPropertyChange(obj, 'foo')– here notifyPropertyChangechecks if the property is being watched, and notifies any observers or computed properties thatfoohas changed – herenotifyPropertyChangethen “marks the object as dirty” – heremarkObjectAsDirtygrabs the underlying “reference” / “tag” for that object and property then marks it as dirty – here- If there was a tag,
markObjectAsDirtycallsensureRunLoopto ensure a runloop is scheduled – here ensureRunLoopchecks if anything has been rendered, and if it has schedules a run loop
Footnotes:
markObjectAsDirtyis 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.setcurrently 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?)