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

feat(scope): add support to skip auto digest in a turn #276

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion lib/core/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class Scope implements Map {
Map<String, List<Function>> _listeners = {};
Scope _nextSibling, _prevSibling, _childHead, _childTail;
bool _isolate = false;
bool _skipAutoDigest = false;
Profiler _perf;


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

// Set up the zone to auto digest this scope.
_zone.onTurnDone = $digest;
_zone.onTurnDone = _autoDigestOnTurnDone;
_zone.onError = (e, s, ls) => _exceptionHandler(e, s);
}

Expand All @@ -92,6 +93,14 @@ class Scope implements Map {
}
}

_autoDigestOnTurnDone() {
if (_skipAutoDigest) {
_skipAutoDigest = false;
} else {
$digest();
}
}

_identical(a, b) =>
identical(a, b) ||
(a is String && b is String && a == b) ||
Expand Down Expand Up @@ -260,6 +269,7 @@ class Scope implements Map {
* auto-digesting scope.
*/
$$verifyDigestWillRun() {
assert(!_skipAutoDigest);
_zone.assertInTurn();
}

Expand Down Expand Up @@ -395,6 +405,31 @@ class Scope implements Map {
}


/**
* Skip running a $digest at the end of this turn.
* The primary use case is to skip the digest in the current VM turn because
* you just scheduled or are otherwise certain of an impending VM turn and the
* digest at the end of that turn is sufficient. You should be able to answer
* "No" to the question "Is there any other code that is aware that this VM
* turn occured and therefore expected a digest?". If your answer is "Yes",
* then you run the risk that the very next VM turn is not for your event and
* now that other code runs in that turn and sees stale values.
*
* You might call this function, for instance, from an event listener where,
* though the event occured, you need to wait for another event before you can
* perform something meaningful. You might schedule that other event,
* set a flag for the handler of the other event to recognize, etc. and then
* call this method to skip the digest this cycle. Note that you should call
* this function *after* you have successfully confirmed that the expected VM
* turn will occur (perhaps by scheduling it) to ensure that the digest
* actually does take place on that turn.
*/
$skipAutoDigest() {
_zone.assertInTurn();
_skipAutoDigest = true;
}


$apply([expr]) {
return _zone.run(() {
var timerId;
Expand Down
5 changes: 4 additions & 1 deletion lib/directive/ng_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ abstract class _InputTextlikeDirective {
inputElement.selectionEnd = end;
};
inputElement.onChange.listen(relaxFnArgs(processValue));
inputElement.onKeyDown.listen((e) => new async.Timer(Duration.ZERO, processValue));
inputElement.onKeyDown.listen((e) {
new async.Timer(Duration.ZERO, processValue);
scope.$skipAutoDigest();
});
}

processValue() {
Expand Down
47 changes: 46 additions & 1 deletion test/core/scope_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import 'dart:convert' show JSON;

main() {
describe(r'Scope', () {
NgZone zone;

noop() {}

beforeEach(module(() {
return (NgZone zone) {
return (NgZone _zone) {
zone = _zone;
zone.onError = (e, s, l) => null;
};
}));
Expand Down Expand Up @@ -74,6 +79,46 @@ main() {
});


describe(r'auto digest', () {
it(r'should auto digest at the end of the turn', inject((Scope $rootScope) {
var digestedValue = 0;
$rootScope.a = 1;
$rootScope.$watch('a', (newValue, oldValue, _this) {
digestedValue = newValue;
});
expect(digestedValue).toEqual(0);
zone.run(noop);
expect(digestedValue).toEqual(1);
}));

it(r'should skip auto digest if requested', inject((Scope $rootScope) {
var digestedValue = 0;
$rootScope.a = 1;
$rootScope.$watch('a', (newValue, oldValue, _this) {
digestedValue = newValue;
});
expect(digestedValue).toEqual(0);
zone.run(() {
$rootScope.$skipAutoDigest();
});
expect(digestedValue).toEqual(0);
zone.run(noop);
expect(digestedValue).toEqual(1);
}));

it(r'should throw exception if asked to skip auto digest outside of a turn',
inject((Scope $rootScope) {
var digestedValue = 0;
$rootScope.a = 1;
$rootScope.$watch('a', (newValue, oldValue, _this) {
digestedValue = newValue;
});
expect(digestedValue).toEqual(0);
expect($rootScope.$skipAutoDigest).toThrow();
}));
});


describe(r'$watch/$digest', () {
it(r'should watch and fire on simple property change', inject((Scope $rootScope) {
var log;
Expand Down