diff --git a/lib/core/scope.dart b/lib/core/scope.dart index b47e8ff69..f891d1d24 100644 --- a/lib/core/scope.dart +++ b/lib/core/scope.dart @@ -680,15 +680,8 @@ class RootScope extends Scope { ChangeLog changeLog; _scopeStats.digestStart(); do { - while (_runAsyncHead != null) { - try { - _runAsyncHead.fn(); - } catch (e, s) { - _exceptionHandler(e, s); - } - _runAsyncHead = _runAsyncHead._next; - } - _runAsyncTail = null; + + int asyncCount = _runAsyncFns(); digestTTL--; count = rootWatchGroup.detectChanges( @@ -704,7 +697,7 @@ class RootScope extends Scope { digestLog = []; changeLog = (e, c, p) => digestLog.add('$e: $c <= $p'); } else { - log.add(digestLog.join(', ')); + log.add("${asyncCount > 0 ? 'async:$asyncCount' : ''}${digestLog.join(', ')}"); digestLog.clear(); } } @@ -713,7 +706,7 @@ class RootScope extends Scope { 'Last $LOG_COUNT iterations:\n${log.join('\n')}'; } _scopeStats.digestLoop(count); - } while (count > 0); + } while (count > 0 || _runAsyncHead != null); } finally { _scopeStats.digestEnd(); _transitionState(STATE_DIGEST, null); @@ -756,7 +749,8 @@ class RootScope extends Scope { if (_domReadHead == null) _stats.domReadEnd(); } _domReadTail = null; - } while (_domWriteHead != null || _domReadHead != null); + _runAsyncFns(); + } while (_domWriteHead != null || _domReadHead != null || _runAsyncHead != null); _stats.flushEnd(); assert((() { _stats.flushAssertStart(); @@ -788,7 +782,7 @@ class RootScope extends Scope { // QUEUES void runAsync(fn()) { - if (_state == STATE_FLUSH || _state == STATE_FLUSH_ASSERT) { + if (_state == STATE_FLUSH_ASSERT) { throw "Scheduling microtasks not allowed in $state state."; } var chain = new _FunctionChain(fn); @@ -799,6 +793,21 @@ class RootScope extends Scope { } } + _runAsyncFns() { + var count = 0; + while (_runAsyncHead != null) { + try { + count++; + _runAsyncHead.fn(); + } catch (e, s) { + _exceptionHandler(e, s); + } + _runAsyncHead = _runAsyncHead._next; + } + _runAsyncTail = null; + return count; + } + void domWrite(fn()) { var chain = new _FunctionChain(fn); if (_domWriteHead == null) { diff --git a/test/core/scope_spec.dart b/test/core/scope_spec.dart index 2530be333..bd2770fd6 100644 --- a/test/core/scope_spec.dart +++ b/test/core/scope_spec.dart @@ -1227,6 +1227,20 @@ void main() { }); + it(r'should detect infinite digest through runAsync', (RootScope rootScope) { + rootScope.context['value'] = () { rootScope.runAsync(() {}); return 'a'; }; + rootScope.watch('value()', (_, __) {}); + + expect(() { + rootScope.digest(); + }).toThrow('Model did not stabilize in 5 digests. ' + 'Last 3 iterations:\n' + 'async:1\n' + 'async:1\n' + 'async:1'); + }); + + it(r'should always call the watchr with newVal and oldVal equal on the first run', inject((RootScope rootScope) { var log = []; @@ -1500,15 +1514,22 @@ void main() { }) ); - it('should not allow microtasks in flush phase', + it('should allow microtasks in flush phase and process them immediatly', async((Logger log, VmTurnZone zone, RootScope scope) { + scope.watch('g()', (_, __) {}); + scope.context['g'] = () { + log('!'); + return 0; + }; + zone.run(() { scope.domWrite(() { - return new Future.value('FAIL'); + log('domWriteA'); + return new Future.value(null).then((_) => scope.domWrite(() => log('domWriteB'))); }); }); expect(log).toEqual( - ['[', '(', 'CATCH: Scheduling microtasks not allowed in flush state.', ')', ']']); + ['[', '!', '!', 'domWriteA', '(', ')', 'domWriteB', /* assert */'!', ']']); }) );