| OLD | NEW |
| 1 part of angular.core_internal; | 1 part of angular.core; |
| 2 | 2 |
| 3 /** | 3 typedef void ZoneOnTurn(); |
| 4 * Handles an [VmTurnZone] onTurnDone event. | |
| 5 */ | |
| 6 typedef void ZoneOnTurnDone(); | |
| 7 | |
| 8 /** | |
| 9 * Handles an [VmTurnZone] onTurnDone event. | |
| 10 */ | |
| 11 typedef void ZoneOnTurnStart(); | |
| 12 | |
| 13 /** | |
| 14 * Handles an [VmTurnZone] onError event. | |
| 15 */ | |
| 16 typedef void ZoneOnError(dynamic error, dynamic stacktrace, | 4 typedef void ZoneOnError(dynamic error, dynamic stacktrace, |
| 17 LongStackTrace longStacktrace); | 5 LongStackTrace longStacktrace); |
| 18 | 6 |
| 19 /** | 7 /** |
| 20 * Contains the locations of async calls across VM turns. | 8 * Contains the locations of runAsync calls across VM turns. |
| 21 */ | 9 */ |
| 22 class LongStackTrace { | 10 class LongStackTrace { |
| 23 final String reason; | 11 final String reason; |
| 24 final dynamic stacktrace; | 12 final dynamic stacktrace; |
| 25 final LongStackTrace parent; | 13 final LongStackTrace parent; |
| 26 | 14 |
| 27 LongStackTrace(this.reason, this.stacktrace, this.parent); | 15 LongStackTrace(this.reason, this.stacktrace, this.parent); |
| 28 | 16 |
| 29 toString() { | 17 toString() { |
| 30 List<String> frames = '${this.stacktrace}'.split('\n') | 18 List<String> frames = '${this.stacktrace}'.split('\n') |
| 31 .where((frame) => | 19 .where((frame) => |
| 32 frame.indexOf('(dart:') == -1 && // skip dart runtime libs | 20 frame.indexOf('(dart:') == -1 && // skip dart runtime libs |
| 33 frame.indexOf('(package:angular/zone.dart') == -1 // skip angular zo
ne | 21 frame.indexOf('(package:angular/zone.dart') == -1 // skip angular zo
ne |
| 34 ).toList()..insert(0, reason); | 22 ).toList()..insert(0, reason); |
| 35 var parent = this.parent == null ? '' : this.parent; | 23 var parent = this.parent == null ? '' : this.parent; |
| 36 return '${frames.join("\n ")}\n$parent'; | 24 return '${frames.join("\n ")}\n$parent'; |
| 37 } | 25 } |
| 38 } | 26 } |
| 39 | 27 |
| 40 /** | 28 /** |
| 41 * A [Zone] wrapper that lets you schedule tasks after its private microtask | 29 * A better zone API which implements onTurnDone. |
| 42 * queue is exhausted but before the next "turn", i.e. event loop iteration. | |
| 43 * This lets you freely schedule microtasks that prepare data, and set an | |
| 44 * [onTurnDone] handler that will consume that data after it's ready but before | |
| 45 * the browser has a chance to re-render. | |
| 46 * The wrapper maintains an "inner" and "outer" [Zone] and a private queue of | |
| 47 * all the microtasks scheduled on the inner [Zone]. | |
| 48 * | |
| 49 * In a typical app, [ngDynamicApp] or [ngStaticApp] will create a singleton | |
| 50 * [VmTurnZone] whose outer [Zone] is the root [Zone] and whose default [onTurnD
one] | |
| 51 * runs the Angular digest. A component may want to inject this singleton if it | |
| 52 * needs to run code _outside_ the Angular digest. | |
| 53 */ | 30 */ |
| 54 class VmTurnZone { | 31 class NgZone { |
| 55 /// an "outer" [Zone], which is the one that created this. | 32 final async.Zone _outerZone; |
| 56 async.Zone _outerZone; | 33 async.Zone _zone; |
| 57 | 34 |
| 58 /// an "inner" [Zone], which is a child of the outer [Zone]. | 35 NgZone() |
| 59 async.Zone _innerZone; | 36 : _outerZone = async.Zone.current |
| 60 | 37 { |
| 61 /** | 38 _zone = _outerZone.fork(specification: new async.ZoneSpecification( |
| 62 * Associates with this | |
| 63 * | |
| 64 * Defaults [onError] to forward errors to the outer [Zone]. | |
| 65 * Defaults [onTurnDone] to a no-op. | |
| 66 */ | |
| 67 VmTurnZone() { | |
| 68 _outerZone = async.Zone.current; | |
| 69 _innerZone = _outerZone.fork(specification: new async.ZoneSpecification( | |
| 70 run: _onRun, | 39 run: _onRun, |
| 71 runUnary: _onRunUnary, | 40 runUnary: _onRunUnary, |
| 72 scheduleMicrotask: _onScheduleMicrotask, | 41 scheduleMicrotask: _onScheduleMicrotask, |
| 73 handleUncaughtError: _uncaughtError | 42 handleUncaughtError: _uncaughtError |
| 74 )); | 43 )); |
| 75 onError = _defaultOnError; | |
| 76 onTurnDone = _defaultOnTurnDone; | |
| 77 onTurnStart = _defaultOnTurnStart; | |
| 78 } | 44 } |
| 79 | 45 |
| 46 |
| 80 List _asyncQueue = []; | 47 List _asyncQueue = []; |
| 81 bool _errorThrownFromOnRun = false; | 48 bool _errorThrownFromOnRun = false; |
| 82 | 49 |
| 83 var _currentlyInTurn = false; | |
| 84 _onRunBase(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()
) { | 50 _onRunBase(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()
) { |
| 85 _runningInTurn++; | 51 _runningInTurn++; |
| 86 try { | 52 try { |
| 87 if (!_currentlyInTurn) { | |
| 88 _currentlyInTurn = true; | |
| 89 delegate.run(zone, onTurnStart); | |
| 90 } | |
| 91 return fn(); | 53 return fn(); |
| 92 } catch (e, s) { | 54 } catch (e, s) { |
| 93 onError(e, s, _longStacktrace); | 55 onError(e, s, _longStacktrace); |
| 94 _errorThrownFromOnRun = true; | 56 _errorThrownFromOnRun = true; |
| 95 rethrow; | 57 rethrow; |
| 96 } finally { | 58 } finally { |
| 97 _runningInTurn--; | 59 _runningInTurn--; |
| 98 if (_runningInTurn == 0) _finishTurn(zone, delegate); | 60 if (_runningInTurn == 0) _finishTurn(zone, delegate); |
| 99 } | 61 } |
| 100 } | 62 } |
| 101 // Called from the parent zone. | 63 // Called from the parent zone. |
| 102 _onRun(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) => | 64 _onRun(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) => |
| 103 _onRunBase(self, delegate, zone, () => delegate.run(zone, fn)); | 65 _onRunBase(self, delegate, zone, () => delegate.run(zone, fn)); |
| 104 | 66 |
| 105 _onRunUnary(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, | 67 _onRunUnary(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, |
| 106 fn(args), args) => | 68 fn(args), args) => |
| 107 _onRunBase(self, delegate, zone, () => delegate.runUnary(zone, fn, args)); | 69 _onRunBase(self, delegate, zone, () => delegate.runUnary(zone, fn, args)); |
| 108 | 70 |
| 109 _onScheduleMicrotask(async.Zone self, async.ZoneDelegate delegate, | 71 _onScheduleMicrotask(async.Zone self, async.ZoneDelegate delegate, |
| 110 async.Zone zone, fn()) { | 72 async.Zone zone, fn()) { |
| 111 _asyncQueue.add(() => delegate.run(zone, fn)); | 73 _asyncQueue.add(() => delegate.run(zone, fn)); |
| 112 if (_runningInTurn == 0 && !_inFinishTurn) _finishTurn(zone, delegate); | 74 if (_runningInTurn == 0 && !_inFinishTurn) _finishTurn(zone, delegate); |
| 113 } | 75 } |
| 114 | 76 |
| 115 _uncaughtError(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, | 77 _uncaughtError(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, |
| 116 e, StackTrace s) { | 78 e, StackTrace s) { |
| 117 if (!_errorThrownFromOnRun) onError(e, s, _longStacktrace); | 79 if (!_errorThrownFromOnRun) onError(e, s, _longStacktrace); |
| 118 _errorThrownFromOnRun = false; | 80 _errorThrownFromOnRun = false; |
| 119 } | 81 } |
| 120 | 82 |
| 121 var _inFinishTurn = false; | 83 var _inFinishTurn = false; |
| 122 _finishTurn(zone, delegate) { | 84 _finishTurn(zone, delegate) { |
| 123 if (_inFinishTurn) return; | 85 if (_inFinishTurn) return; |
| 124 _inFinishTurn = true; | 86 _inFinishTurn = true; |
| 125 try { | 87 try { |
| 126 // Two loops here: the inner one runs all queued microtasks, | 88 // Two loops here: the inner one runs all queued microtasks, |
| 127 // the outer runs onTurnDone (e.g. scope.digest) and then | 89 // the outer runs onTurnDone (e.g. scope.digest) and then |
| 128 // any microtasks which may have been queued from onTurnDone. | 90 // any microtasks which may have been queued from onTurnDone. |
| 129 // If any microtasks were scheduled during onTurnDone, onTurnStart | |
| 130 // will be executed before those microtasks. | |
| 131 do { | 91 do { |
| 132 if (!_currentlyInTurn) { | |
| 133 _currentlyInTurn = true; | |
| 134 delegate.run(zone, onTurnStart); | |
| 135 } | |
| 136 while (!_asyncQueue.isEmpty) { | 92 while (!_asyncQueue.isEmpty) { |
| 137 delegate.run(zone, _asyncQueue.removeAt(0)); | 93 delegate.run(zone, _asyncQueue.removeAt(0)); |
| 138 } | 94 } |
| 139 delegate.run(zone, onTurnDone); | 95 delegate.run(zone, onTurnDone); |
| 140 _currentlyInTurn = false; | |
| 141 } while (!_asyncQueue.isEmpty); | 96 } while (!_asyncQueue.isEmpty); |
| 142 } catch (e, s) { | 97 } catch (e, s) { |
| 143 onError(e, s, _longStacktrace); | 98 onError(e, s, _longStacktrace); |
| 144 _errorThrownFromOnRun = true; | 99 _errorThrownFromOnRun = true; |
| 145 rethrow; | 100 rethrow; |
| 146 } finally { | 101 } finally { |
| 147 _inFinishTurn = false; | 102 _inFinishTurn = false; |
| 148 } | 103 } |
| 149 } | 104 } |
| 150 | 105 |
| 151 int _runningInTurn = 0; | 106 int _runningInTurn = 0; |
| 152 | 107 |
| 153 /** | 108 /** |
| 154 * Called with any errors from the inner zone. | 109 * A function called with any errors from the zone. |
| 155 */ | 110 */ |
| 156 ZoneOnError onError; | 111 var onError = (e, s, ls) => null; |
| 157 | |
| 158 /// Prevent silently ignoring uncaught exceptions by forwarding such exception
s to the outer zone. | |
| 159 void _defaultOnError(dynamic e, dynamic s, LongStackTrace ls) => | |
| 160 _outerZone.handleUncaughtError(e, s); | |
| 161 | 112 |
| 162 /** | 113 /** |
| 163 * Called at the beginning of each VM turn in which inner zone code runs. | 114 * A function that is called at the end of each VM turn in which the |
| 164 * "At the beginning" means before any of the microtasks from the private | 115 * in-zone code or any runAsync callbacks were run. |
| 165 * microtask queue of the inner zone is executed. Notes | |
| 166 * - [onTurnStart] runs repeatedly until no more microstasks are scheduled | |
| 167 * within [onTurnStart], [run] or [onTurnDone]. You usually don't want it to | |
| 168 * schedule any. For example, if its first line of code is `new Future.valu
e()`, | |
| 169 * the turn will _never_ end. | |
| 170 */ | 116 */ |
| 171 ZoneOnTurnStart onTurnStart; | 117 var onTurnDone = () => null; // Type was ZoneOnTurn: dartbug 13519 |
| 172 void _defaultOnTurnStart() => null; | |
| 173 | |
| 174 | 118 |
| 175 /** | 119 /** |
| 176 * Called at the end of each VM turn in which inner zone code runs. | 120 * A function that is called when uncaught errors are thrown inside the zone. |
| 177 * "At the end" means after the private microtask queue of the inner zone is | |
| 178 * exhausted but before the next VM turn. Notes | |
| 179 * - This won't wait for microtasks scheduled in zones other than the inner | |
| 180 * zone, e.g. those scheduled with [runOutsideAngular]. | |
| 181 * - [onTurnDone] runs repeatedly until no more tasks are scheduled within | |
| 182 * [onTurnStart], [run] or [onTurnDone]. You usually don't want it to | |
| 183 * schedule any. For example, if its first line of code is `new Future.valu
e()`, | |
| 184 * the turn will _never_ end. | |
| 185 */ | 121 */ |
| 186 ZoneOnTurnDone onTurnDone; | 122 // var onError = (dynamic e, dynamic s, LongStackTrace ls) => print('EXCEPTION
: $e\n$s\n$ls'); |
| 187 void _defaultOnTurnDone() => null; | 123 // Type was ZoneOnError: dartbug 13519 |
| 188 | 124 |
| 189 LongStackTrace _longStacktrace = null; | 125 LongStackTrace _longStacktrace = null; |
| 190 | 126 |
| 191 LongStackTrace _getLongStacktrace(name) { | 127 LongStackTrace _getLongStacktrace(name) { |
| 192 var shortStacktrace = 'Long-stacktraces supressed in production.'; | 128 var shortStacktrace = 'Long-stacktraces supressed in production.'; |
| 193 assert((shortStacktrace = _getStacktrace()) != null); | 129 assert((shortStacktrace = _getStacktrace()) != null); |
| 194 return new LongStackTrace(name, shortStacktrace, _longStacktrace); | 130 return new LongStackTrace(name, shortStacktrace, _longStacktrace); |
| 195 } | 131 } |
| 196 | 132 |
| 197 StackTrace _getStacktrace() { | 133 _getStacktrace() { |
| 198 try { | 134 try { |
| 199 throw []; | 135 throw []; |
| 200 } catch (e, s) { | 136 } catch (e, s) { |
| 201 return s; | 137 return s; |
| 202 } | 138 } |
| 203 } | 139 } |
| 204 | 140 |
| 205 /** | 141 /** |
| 206 * Runs [body] in the inner zone and returns whatever it returns. | 142 * Runs the provided function in the zone. Any runAsync calls (e.g. futures) |
| 143 * will also be run in this zone. |
| 144 * |
| 145 * Returns the return value of body. |
| 207 */ | 146 */ |
| 208 dynamic run(body()) => _innerZone.run(body); | 147 run(body()) => _zone.run(body); |
| 209 | 148 |
| 210 /** | 149 /** |
| 211 * Runs [body] in the outer zone and returns whatever it returns. | 150 * Allows one to escape the auto-digest mechanism of Angular. |
| 212 * In a typical app where the inner zone is the Angular zone, this allows | |
| 213 * one to escape Angular's auto-digest mechanism. | |
| 214 * | 151 * |
| 215 * myFunction(VmTurnZone zone, Element element) { | 152 * myFunction(NgZone zone, Element element) { |
| 216 * element.onClick.listen(() { | 153 * element.onClick.listen(() { |
| 217 * // auto-digest will run after element click. | 154 * // auto-digest will run after element click. |
| 218 * }); | 155 * }); |
| 219 * zone.runOutsideAngular(() { | 156 * zone.runOutsideAngular(() { |
| 220 * element.onMouseMove.listen(() { | 157 * element.onMouseMove.listen(() { |
| 221 * // auto-digest will NOT run after mouse move | 158 * // auto-digest will NOT run after mouse move |
| 222 * }); | 159 * }); |
| 223 * }); | 160 * }); |
| 224 * } | 161 * } |
| 225 */ | 162 */ |
| 226 dynamic runOutsideAngular(body()) => _outerZone.run(body); | 163 runOutsideAngular(body()) => _outerZone.run(body); |
| 227 | 164 |
| 228 /** | 165 assertInTurn() { |
| 229 * Throws an [AssertionError] if no task is currently running in the inner | |
| 230 * zone. In a typical app where the inner zone is the Angular zone, this can | |
| 231 * be used to assert that the digest will indeed run at the end of the current | |
| 232 * turn. | |
| 233 */ | |
| 234 void assertInTurn() { | |
| 235 assert(_runningInTurn > 0 || _inFinishTurn); | 166 assert(_runningInTurn > 0 || _inFinishTurn); |
| 236 } | 167 } |
| 237 | 168 |
| 238 /** | 169 assertInZone() { |
| 239 * Same as [assertInTurn]. | |
| 240 */ | |
| 241 void assertInZone() { | |
| 242 assertInTurn(); | 170 assertInTurn(); |
| 243 } | 171 } |
| 244 } | 172 } |
| OLD | NEW |