Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 82da8e5

Browse files
committed
feat(scope): add support to skip auto digest in a turn
Closes #235
1 parent 6ca5d73 commit 82da8e5

File tree

3 files changed

+86
-3
lines changed

3 files changed

+86
-3
lines changed

lib/core/scope.dart

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class Scope implements Map {
5555
Map<String, List<Function>> _listeners = {};
5656
Scope _nextSibling, _prevSibling, _childHead, _childTail;
5757
bool _isolate = false;
58+
bool _skipAutoDigest = false;
5859
Profiler _perf;
5960

6061

@@ -68,7 +69,7 @@ class Scope implements Map {
6869
_outerAsyncQueue = [];
6970

7071
// Set up the zone to auto digest this scope.
71-
_zone.onTurnDone = $digest;
72+
_zone.onTurnDone = _autoDigestOnTurnDone;
7273
_zone.onError = (e, s, ls) => _exceptionHandler(e, s);
7374
}
7475

@@ -92,6 +93,14 @@ class Scope implements Map {
9293
}
9394
}
9495

96+
_autoDigestOnTurnDone() {
97+
if (_skipAutoDigest) {
98+
_skipAutoDigest = false;
99+
} else {
100+
$digest();
101+
}
102+
}
103+
95104
_identical(a, b) =>
96105
identical(a, b) ||
97106
(a is String && b is String && a == b) ||
@@ -260,6 +269,7 @@ class Scope implements Map {
260269
* auto-digesting scope.
261270
*/
262271
$$verifyDigestWillRun() {
272+
assert(!_skipAutoDigest);
263273
_zone.assertInTurn();
264274
}
265275

@@ -395,6 +405,31 @@ class Scope implements Map {
395405
}
396406

397407

408+
/**
409+
* Skip running a $digest at the end of this turn.
410+
* The primary use case is to skip the digest in the current VM turn because
411+
* you just scheduled or are otherwise certain of an impending VM turn and the
412+
* digest at the end of that turn is sufficient. You should be able to answer
413+
* "No" to the question "Is there any other code that is aware that this VM
414+
* turn occured and therefore expected a digest?". If your answer is "Yes",
415+
* then you run the risk that the very next VM turn is not for your event and
416+
* now that other code runs in that turn and sees stale values.
417+
*
418+
* You might call this function, for instance, from an event listener where,
419+
* though the event occured, you need to wait for another event before you can
420+
* perform something meaningful. You might schedule that other event,
421+
* set a flag for the handler of the other event to recognize, etc. and then
422+
* call this method to skip the digest this cycle. Note that you should call
423+
* this function *after* you have successfully confirmed that the expected VM
424+
* turn will occur (perhaps by scheduling it) to ensure that the digest
425+
* actually does take place on that turn.
426+
*/
427+
$skipAutoDigest() {
428+
_zone.assertInTurn();
429+
_skipAutoDigest = true;
430+
}
431+
432+
398433
$apply([expr]) {
399434
return _zone.run(() {
400435
var timerId;

lib/directive/ng_model.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,10 @@ abstract class _InputTextlikeDirective {
109109
inputElement.selectionEnd = end;
110110
};
111111
inputElement.onChange.listen(relaxFnArgs(processValue));
112-
inputElement.onKeyDown.listen((e) => new async.Timer(Duration.ZERO, processValue));
112+
inputElement.onKeyDown.listen((e) {
113+
new async.Timer(Duration.ZERO, processValue);
114+
scope.$skipAutoDigest();
115+
});
113116
}
114117

115118
processValue() {

test/core/scope_spec.dart

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ import 'dart:convert' show JSON;
66

77
main() {
88
describe(r'Scope', () {
9+
NgZone zone;
10+
11+
noop() {}
12+
913
beforeEach(module(() {
10-
return (NgZone zone) {
14+
return (NgZone _zone) {
15+
zone = _zone;
1116
zone.onError = (e, s, l) => null;
1217
};
1318
}));
@@ -74,6 +79,46 @@ main() {
7479
});
7580

7681

82+
describe(r'auto digest', () {
83+
it(r'should auto digest at the end of the turn', inject((Scope $rootScope) {
84+
var digestedValue = 0;
85+
$rootScope.a = 1;
86+
$rootScope.$watch('a', (newValue, oldValue, _this) {
87+
digestedValue = newValue;
88+
});
89+
expect(digestedValue).toEqual(0);
90+
zone.run(noop);
91+
expect(digestedValue).toEqual(1);
92+
}));
93+
94+
it(r'should skip auto digest if requested', inject((Scope $rootScope) {
95+
var digestedValue = 0;
96+
$rootScope.a = 1;
97+
$rootScope.$watch('a', (newValue, oldValue, _this) {
98+
digestedValue = newValue;
99+
});
100+
expect(digestedValue).toEqual(0);
101+
zone.run(() {
102+
$rootScope.$skipAutoDigest();
103+
});
104+
expect(digestedValue).toEqual(0);
105+
zone.run(noop);
106+
expect(digestedValue).toEqual(1);
107+
}));
108+
109+
it(r'should throw exception if asked to skip auto digest outside of a turn',
110+
inject((Scope $rootScope) {
111+
var digestedValue = 0;
112+
$rootScope.a = 1;
113+
$rootScope.$watch('a', (newValue, oldValue, _this) {
114+
digestedValue = newValue;
115+
});
116+
expect(digestedValue).toEqual(0);
117+
expect($rootScope.$skipAutoDigest).toThrow();
118+
}));
119+
});
120+
121+
77122
describe(r'$watch/$digest', () {
78123
it(r'should watch and fire on simple property change', inject((Scope $rootScope) {
79124
var log;

0 commit comments

Comments
 (0)