diff --git a/lib/core/zone.dart b/lib/core/zone.dart index a201331dc..63b60898a 100644 --- a/lib/core/zone.dart +++ b/lib/core/zone.dart @@ -10,6 +10,12 @@ typedef void ZoneOnTurnDone(); */ typedef void ZoneOnTurnStart(); +/** + * Handles a [VmTurnZone] defaultOnScheduleMicrotask. + */ +typedef void ZoneScheduleMicrotask(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, + fn()); + /** * Handles a [VmTurnZone] onError event. */ @@ -58,6 +64,11 @@ class VmTurnZone { /// an "inner" [Zone], which is a child of the outer [Zone]. async.Zone _innerZone; + /** + * + */ + ZoneScheduleMicrotask onScheduleMicrotask; + /** * Associates with this * @@ -78,6 +89,7 @@ class VmTurnZone { onError = _defaultOnError; onTurnDone = _defaultOnTurnDone; onTurnStart = _defaultOnTurnStart; + onScheduleMicrotask = _defaultOnScheduleMicrotask; } List _asyncQueue = []; @@ -111,6 +123,11 @@ class VmTurnZone { _onScheduleMicrotask(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) { + onScheduleMicrotask(self, delegate, zone, fn); + } + + _defaultOnScheduleMicrotask(async.Zone self, async.ZoneDelegate delegate, + async.Zone zone, fn()) { _asyncQueue.add(() => delegate.run(zone, fn)); if (_runningInTurn == 0 && !_inFinishTurn) _finishTurn(zone, delegate); } diff --git a/test/core/zone_spec.dart b/test/core/zone_spec.dart index 9e33ae62d..10fa32f78 100644 --- a/test/core/zone_spec.dart +++ b/test/core/zone_spec.dart @@ -435,5 +435,112 @@ void main() { microLeap(); })).toThrow('ssertion'); // Support both dart2js and the VM with half a word. }); + + group('microtask scheduler', () { + + it('should execute microtask scheduled in onTurnDone before onTurnDone is complete', + async((Logger log) { + var microtaskResult = false; + zone.onTurnStart = () { + log('onTurnStart'); + }; + zone.onTurnDone = () { + log('onTurnDone(begin)'); + scheduleMicrotask(() { + log('executeMicrotask'); + return true; + }); + log('onTurnDone(end)'); + }; + zone.onScheduleMicrotask = (_, __, ___, microTaskFn) { + log('onScheduleMicrotask(begin)'); + microtaskResult = microTaskFn(); + log('onScheduleMicrotask(end)'); + }; + zone.run(() { + log('run'); + }); + + expect(log.result()).toEqual('onTurnStart; run; onTurnDone(begin); ' + 'onScheduleMicrotask(begin); executeMicrotask; onScheduleMicrotask(end); onTurnDone(end)' + ); + expect(microtaskResult).toBeTruthy(); + })); + + it('should work with future scheduled in onTurnDone', async((Logger log) { + zone.onTurnStart = () { + log('onTurnStart'); + }; + zone.onTurnDone = () { + log('onTurnDone(begin)'); + new Future.value('async').then((v) { + log('executed ${v}'); + }); + log('onTurnDone(end)'); + }; + zone.onScheduleMicrotask = (_, __, ___, microTaskFn) { + log('onScheduleMicrotask(begin)'); + microTaskFn(); + log('onScheduleMicrotask(end)'); + }; + zone.run(() { + log('run'); + }); + + expect(log.result()).toEqual('onTurnStart; run; onTurnDone(begin); ' + 'onScheduleMicrotask(begin); onScheduleMicrotask(end); onScheduleMicrotask(begin);' + ' executed async; onScheduleMicrotask(end); onTurnDone(end)'); + })); + + it('should execute microtask scheduled in run before onTurnDone starts', + async((Logger log) { + zone.onTurnStart = () { + log('onTurnStart'); + }; + zone.onTurnDone = () { + log('onTurnDone'); + }; + zone.onScheduleMicrotask = (_, __, ___, microTaskFn) { + log('onScheduleMicrotask(begin)'); + microTaskFn(); + log('onScheduleMicrotask(end)'); + }; + zone.run(() { + log('run'); + scheduleMicrotask(() { + log('executeMicrotask'); + return true; + }); + }); + + expect(log.result()).toEqual('onTurnStart; run; onScheduleMicrotask(begin);' + ' executeMicrotask; onScheduleMicrotask(end); onTurnDone'); + })); + + it('should execute microtask scheduled in onTurnStart before run', + async((Logger log) { + zone.onTurnStart = () { + log('onTurnStart'); + scheduleMicrotask(() { + log('executeMicrotask'); + }); + }; + zone.onTurnDone = () { + log('onTurnDone'); + }; + zone.onScheduleMicrotask = (_, __, ___, microTaskFn) { + log('onScheduleMicrotask(begin)'); + microTaskFn(); + log('onScheduleMicrotask(end)'); + }; + zone.run(() { + log('run'); + }); + + expect(log.result()).toEqual('onTurnStart; onScheduleMicrotask(begin); executeMicrotask;' + ' onScheduleMicrotask(end); run; onTurnDone'); + })); + + }); }); }