Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

fix(scopes): correctly meassure perf impact of one-time expressions #124

Closed
wants to merge 2 commits into from

Conversation

gkalpak
Copy link
Member

@gkalpak gkalpak commented Mar 30, 2017

Previously, one-time expressions were supposed to not trigger perf timers. There
were a couple of issues with the implementation:

  1. One-time expressions were not detected as such if there was whitespace before
    the ::.
  2. It depended on internal AngularJS implementation details in a way that would
    fail to identify certain usecases (e.g. multiple interpolations in a single
    attribute value or node text).
  3. Time spent on the initial evaluation of the one-time expressions would not be
    accounted for, but could still impact the duration of the first $digest.
  4. While it is not common, there are cases were one-time expressions don't
    settle immediately. In such cases, their impact on the $digest should not
    be ignored.

This commit fixes the above issues by ensuring that only the "final" call to
$watch is intercepted (in contrast to intermediate calls that might call a
$$watchDelegate, which will then create the actual $watcher). One-time
expressions are intercepted normally and stop triggering perf times as soon as
their unwatch function is called.

Although the dependency on internal AngularJS implementation details could not
be avoided, the new implementation is a little more robust, as it relies on
fewer and less brittle details :D

Fixes #109
Closes #122

Previously, one-time expressions were supposed to not trigger perf timers. There
were a couple of issues with the implementation:

1. One-time expressions were not detected as such if there was whitespace before
   the `::`.
2. It depended on internal AngularJS implementation details in a way that would
   fail to identify certain usecases (e.g. multiple interpolations in a single
   attribute value or node text).
3. Time spent on the initial evaluation of the one-time expressions would not be
   accounted for, but could still impact the duration of the first `$digest`.
4. While it is not common, there are cases were one-time expressions don't
   settle immediately. In such cases, their impact on the `$digest` should not
   be ignored.

This commit fixes the above issues by ensuring that only the "final" call to
`$watch` is intercepted (in contrast to intermediate calls that might call a
`$$watchDelegate`, which will then create the actual `$watch`er). One-time
expressions are intercepted normally and stop triggering perf times as soon as
their `unwatch` function is called.

Although the dependency on internal AngularJS implementation details could not
be avoided, the new implementation is a little more robust, as it relies on
fewer and less brittle details :D

Fixes angular#109
Closes angular#122
scope.$apply('c.d = "foo"');
expect(calls.count()).toBe(1);
expect(countWatchEventsFor(0)).toBe(6); // Change: 2 loops
expect(reactions).toEqual([2, 2, 2]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth a comment here, indicating that this is the first time the one time bindings have a value, which is why the reactions are called for a second time.

'<span>{{::c.d}}</span>' +
'<span>{{ ::c.d }}</span>' +
'<span>{{ :: c.d }}</span>' +
'<span>{{::c.d}}{{ ::c.d }}{{ :: c.d }}</span>' +

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it useful to check these for non-interpolation bindings such as ng-if as well?

Copy link

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two minor suggestions

@gkalpak
Copy link
Member Author

gkalpak commented Mar 30, 2017

@petebacondarwin, PTAL.

@gkalpak gkalpak closed this in 241aecb Mar 30, 2017
gkalpak added a commit that referenced this pull request Mar 30, 2017
Previously, one-time expressions were supposed to not trigger perf timers. There
were a couple of issues with the implementation:

1. One-time expressions were not detected as such if there was whitespace before
   the `::`.
2. It depended on internal AngularJS implementation details in a way that would
   fail to identify certain usecases (e.g. multiple interpolations in a single
   attribute value or node text).
3. Time spent on the initial evaluation of the one-time expressions would not be
   accounted for, but could still impact the duration of the first `$digest`.
4. While it is not common, there are cases were one-time expressions don't
   settle immediately. In such cases, their impact on the `$digest` should not
   be ignored.

This commit fixes the above issues by ensuring that only the "final" call to
`$watch` is intercepted (in contrast to intermediate calls that might call a
`$$watchDelegate`, which will then create the actual `$watch`er). One-time
expressions are intercepted normally and stop triggering perf times as soon as
their `unwatch` function is called.

Although the dependency on internal AngularJS implementation details could not
be avoided, the new implementation is a little more robust, as it relies on
fewer and less brittle details :D

Fixes #109
Closes #122

Closes #124
@gkalpak gkalpak deleted the fix-scopes-one-time-watchers branch April 27, 2017 17:04
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

$watch override makes one-time bindings in expressions to never clear the associated watch
2 participants