Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.async; | 5 part of dart.async; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * A Zone represents the asynchronous version of a dynamic extent. Asynchronous | 8 * A Zone represents the asynchronous version of a dynamic extent. Asynchronous |
| 9 * callbacks are executed in the zone they have been queued in. For example, | 9 * callbacks are executed in the zone they have been queued in. For example, |
| 10 * the callback of a `future.then` is executed in the same zone as the one where | 10 * the callback of a `future.then` is executed in the same zone as the one where |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 * | 31 * |
| 32 * The main purpose of this method is to allow `this` to attach debugging | 32 * The main purpose of this method is to allow `this` to attach debugging |
| 33 * information to the returned zone. | 33 * information to the returned zone. |
| 34 */ | 34 */ |
| 35 _Zone fork(); | 35 _Zone fork(); |
| 36 | 36 |
| 37 /** | 37 /** |
| 38 * Tells the zone that it needs to wait for one more callback before it is | 38 * Tells the zone that it needs to wait for one more callback before it is |
| 39 * done. | 39 * done. |
| 40 * | 40 * |
| 41 * Use [executeCallback] or [unexpectCallback] when the callback is executed | 41 * Use [executeCallback] or [cancelCallbackExpectation] when the callback is e xecuted |
| 42 * (or canceled). | 42 * (or canceled). |
| 43 */ | 43 */ |
| 44 void expectCallback(); | 44 void expectCallback(); |
| 45 | 45 |
| 46 /** | 46 /** |
| 47 * Tells the zone not to wait for a callback anymore. | 47 * Tells the zone not to wait for a callback anymore. |
| 48 * | 48 * |
| 49 * Prefer calling [executeCallback], instead. This method is mostly useful | 49 * Prefer calling [executeCallback], instead. This method is mostly useful |
| 50 * for repeated callbacks (for example with [Timer.periodic]). In this case | 50 * for repeated callbacks (for example with [Timer.periodic]). In this case |
| 51 * one should should call [expectCallback] when the repeated callback is | 51 * one should should call [expectCallback] when the repeated callback is |
| 52 * initiated, and [unexpectCallback] when the [Timer] is canceled. | 52 * initiated, and [cancelCallbackExpectation] when the [Timer] is canceled. |
| 53 */ | 53 */ |
| 54 void unexpectCallback(); | 54 void cancelCallbackExpectation(); |
| 55 | 55 |
| 56 /** | 56 /** |
| 57 * Executes the given callback in this zone. | 57 * Executes the given callback in this zone. |
| 58 * | 58 * |
| 59 * Decrements the number of callbacks this zone is waiting for (see | 59 * Decrements the number of callbacks this zone is waiting for (see |
| 60 * [expectCallback]). | 60 * [expectCallback]). |
| 61 */ | 61 */ |
| 62 void executeCallback(void fun()); | 62 void executeCallback(void fun()); |
| 63 | 63 |
| 64 /** | 64 /** |
| 65 * Same as [executeCallback] but catches uncaught errors and gives them to | 65 * Same as [executeCallback] but catches uncaught errors and gives them to |
| 66 * [handleUncaughtError]. | 66 * [handleUncaughtError]. |
| 67 */ | 67 */ |
| 68 void executeCallbackGuarded(void fun()); | 68 void executeCallbackGuarded(void fun()); |
| 69 | 69 |
| 70 /** | 70 /** |
| 71 * Same as [executeCallback] but does not decrement the number of | 71 * Same as [executeCallback] but does not decrement the number of |
| 72 * callbacks this zone is waiting for (see [expectCallback]). | 72 * callbacks this zone is waiting for (see [expectCallback]). |
| 73 */ | 73 */ |
| 74 void executePeriodicCallback(void fun()); | 74 void executePeriodicCallback(void fun()); |
| 75 | 75 |
| 76 /** | 76 /** |
| 77 * Same as [executePeriodicCallback] but catches uncaught errors and gives | 77 * Same as [executePeriodicCallback] but catches uncaught errors and gives |
| 78 * them to [handleUncaughtError]. | 78 * them to [handleUncaughtError]. |
| 79 */ | 79 */ |
| 80 void executeGuardedPeriodicCallback(void fun()); | 80 void executePeriodicCallbackGuarded(void fun()); |
| 81 | 81 |
| 82 /** | 82 /** |
| 83 * Runs [fun] asynchronously in this zone. | 83 * Runs [fun] asynchronously in this zone. |
| 84 */ | 84 */ |
| 85 void runAsync(void fun()); | 85 void runAsync(void fun()); |
| 86 | 86 |
| 87 /** | 87 /** |
| 88 * Creates a Timer where the callback is executed in this zone. | 88 * Creates a Timer where the callback is executed in this zone. |
| 89 */ | 89 */ |
| 90 Timer createTimer(Duration duration, void callback()); | 90 Timer createTimer(Duration duration, void callback()); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 final _Zone _parentZone; | 124 final _Zone _parentZone; |
| 125 | 125 |
| 126 /// The children of this zone. A child's [_parentZone] is `this`. | 126 /// The children of this zone. A child's [_parentZone] is `this`. |
| 127 // TODO(floitsch): this should be a double-linked list. | 127 // TODO(floitsch): this should be a double-linked list. |
| 128 final List<_Zone> _children = <_Zone>[]; | 128 final List<_Zone> _children = <_Zone>[]; |
| 129 | 129 |
| 130 /// The number of outstanding (asynchronous) callbacks. As long as the | 130 /// The number of outstanding (asynchronous) callbacks. As long as the |
| 131 /// number is greater than 0 it means that the zone is not done yet. | 131 /// number is greater than 0 it means that the zone is not done yet. |
| 132 int _openCallbacks = 0; | 132 int _openCallbacks = 0; |
| 133 | 133 |
| 134 bool _isExecutingCallback = false; | |
| 135 | |
| 134 _ZoneBase(this._parentZone) { | 136 _ZoneBase(this._parentZone) { |
| 135 _parentZone._addChild(this); | 137 _parentZone._addChild(this); |
| 136 } | 138 } |
| 137 | 139 |
| 138 _ZoneBase._defaultZone() : _parentZone = null { | 140 _ZoneBase._defaultZone() : _parentZone = null { |
| 139 assert(this is _DefaultZone); | 141 assert(this is _DefaultZone); |
| 140 } | 142 } |
| 141 | 143 |
| 142 _Zone get _errorZone => _parentZone._errorZone; | 144 _Zone get _errorZone => _parentZone._errorZone; |
| 143 | 145 |
| 144 void handleUncaughtError(error) { | 146 void handleUncaughtError(error) { |
| 145 _parentZone.handleUncaughtError(error); | 147 _parentZone.handleUncaughtError(error); |
| 146 } | 148 } |
| 147 | 149 |
| 148 bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone; | 150 bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone; |
| 149 | 151 |
| 150 _Zone fork() => this; | 152 _Zone fork() => this; |
| 151 | 153 |
| 152 expectCallback() => _openCallbacks++; | 154 expectCallback() => _openCallbacks++; |
| 153 | 155 |
| 154 unexpectCallback() { | 156 cancelCallbackExpectation() { |
| 155 _openCallbacks--; | 157 _openCallbacks--; |
| 156 _checkIfDone(); | 158 _checkIfDone(); |
| 157 } | 159 } |
| 158 | 160 |
| 159 /** | 161 /** |
| 160 * Cleans up this zone when it is done. | 162 * Cleans up this zone when it is done. |
| 161 * | 163 * |
| 162 * This releases internal memore structures that are no longer necessary. | 164 * This releases internal memore structures that are no longer necessary. |
| 163 * | 165 * |
| 164 * A zone is done when its dynamic extent has finished executing and | 166 * A zone is done when its dynamic extent has finished executing and |
| 165 * there are no outstanding asynchronous callbacks. | 167 * there are no outstanding asynchronous callbacks. |
| 166 */ | 168 */ |
| 167 _dispose() { | 169 _dispose() { |
| 168 if (_parentZone != null) { | 170 if (_parentZone != null) { |
| 169 _parentZone._removeChild(this); | 171 _parentZone._removeChild(this); |
| 170 } | 172 } |
| 171 } | 173 } |
| 172 | 174 |
| 173 /** | 175 /** |
| 174 * Checks if the zone is done and doesn't have any outstanding callbacks | 176 * Checks if the zone is done and doesn't have any outstanding callbacks |
| 175 * anymore. | 177 * anymore. |
| 176 * | 178 * |
| 177 * This method is called when an operation has decremented the | 179 * This method is called when an operation has decremented the |
| 178 * outstanding-callback count, or when a child has been removed. | 180 * outstanding-callback count, or when a child has been removed. |
| 179 */ | 181 */ |
| 180 void _checkIfDone() { | 182 void _checkIfDone() { |
| 181 if (_openCallbacks == 0 && _children.isEmpty) { | 183 if (!_isExecutingCallback && _openCallbacks == 0 && _children.isEmpty) { |
| 182 _dispose(); | 184 _dispose(); |
| 183 } | 185 } |
| 184 } | 186 } |
| 185 | 187 |
| 186 /** | 188 /** |
| 187 * Executes the given callback in this zone. | 189 * Executes the given callback in this zone. |
| 188 * | 190 * |
| 189 * Decrements the open-callback counter and checks (after the call) if the | 191 * Decrements the open-callback counter and checks (after the call) if the |
| 190 * zone is done. | 192 * zone is done. |
| 191 */ | 193 */ |
| 192 void executeCallback(void fun()) { | 194 void executeCallback(void fun()) { |
| 193 _openCallbacks--; | 195 _openCallbacks--; |
| 194 _runInZone(fun); | 196 _runUnguarded(fun); |
|
Lasse Reichstein Nielsen
2013/06/13 11:58:47
That sounds like you could just have run fun direc
floitsch
2013/06/13 13:36:51
Done.
| |
| 195 } | 197 } |
| 196 | 198 |
| 197 /** | 199 /** |
| 198 * Same as [executeCallback] but catches uncaught errors and gives them to | 200 * Same as [executeCallback] but catches uncaught errors and gives them to |
| 199 * [handleUncaughtError]. | 201 * [handleUncaughtError]. |
| 200 */ | 202 */ |
| 201 void executeCallbackGuarded(void fun()) { | 203 void executeCallbackGuarded(void fun()) { |
| 202 _openCallbacks--; | 204 _openCallbacks--; |
| 203 _runGuarded(fun); | 205 _runGuarded(fun); |
| 204 } | 206 } |
| 205 | 207 |
| 206 /** | 208 /** |
| 207 * Same as [executeCallback] but doesn't decrement the open-callback counter. | 209 * Same as [executeCallback] but doesn't decrement the open-callback counter. |
| 208 */ | 210 */ |
| 209 void executePeriodicCallback(void fun()) { | 211 void executePeriodicCallback(void fun()) { |
| 210 _runInZone(fun); | 212 _runUnguarded(fun); |
| 211 } | 213 } |
| 212 | 214 |
| 213 /** | 215 /** |
| 214 * Same as [executePeriodicCallback] but catches uncaught errors and gives | 216 * Same as [executePeriodicCallback] but catches uncaught errors and gives |
| 215 * them to [handleUncaughtError]. | 217 * them to [handleUncaughtError]. |
| 216 */ | 218 */ |
| 217 void executeGuardedPeriodicCallback(void fun()) { | 219 void executePeriodicCallbackGuarded(void fun()) { |
| 218 _runGuarded(fun); | 220 _runGuarded(fun); |
| 219 } | 221 } |
| 220 | 222 |
| 221 _runInZone(fun()) { | 223 _runInZone(fun(), bool handleUncaught) { |
| 222 if (identical(_Zone._current, this) && _openCallbacks != 0) return fun(); | 224 if (identical(_Zone._current, this) |
| 225 && !handleUncaught | |
| 226 && _isExecutingCallback) { | |
| 227 // No need to go through a try/catch. | |
| 228 return fun(); | |
| 229 } | |
| 223 | 230 |
| 224 _Zone oldZone = _Zone._current; | 231 _Zone oldZone = _Zone._current; |
| 225 _Zone._current = this; | 232 _Zone._current = this; |
| 226 // While we are executing the function we don't want to have other | 233 // While we are executing the function we don't want to have other |
| 227 // synchronous calls to think that they closed the zone. By incrementing | 234 // synchronous calls to think that they closed the zone. By incrementing |
| 228 // the _openCallbacks count we make sure that their test will fail. | 235 // the _openCallbacks count we make sure that their test will fail. |
| 229 // As a side effect it will make nested calls faster since they are | 236 // As a side effect it will make nested calls faster since they are |
| 230 // (probably) in the same zone and have an _openCallbacks > 0. | 237 // (probably) in the same zone and have an _openCallbacks > 0. |
| 231 _openCallbacks++; | 238 bool oldIsExecuting = _isExecutingCallback; |
| 239 _isExecutingCallback = true; | |
|
Lasse Reichstein Nielsen
2013/06/13 11:58:47
So you can have nested callbacks?
floitsch
2013/06/13 13:36:51
yes. For example a future-callback can invoke a sy
| |
| 232 try { | 240 try { |
| 233 return fun(); | 241 return fun(); |
| 242 } catch(e, s) { | |
|
Lasse Reichstein Nielsen
2013/06/13 11:58:47
Seems overkill to catch and rethrow, but I guess i
floitsch
2013/06/13 13:36:51
Otherwise I would need to duplicate the try/catch.
| |
| 243 if (handleUncaught) { | |
| 244 handleUncaughtError(_asyncError(e, s)); | |
| 245 } else { | |
| 246 rethrow; | |
| 247 } | |
| 234 } finally { | 248 } finally { |
| 235 _openCallbacks--; | 249 _isExecutingCallback = oldIsExecuting; |
| 236 _Zone._current = oldZone; | 250 _Zone._current = oldZone; |
| 237 _checkIfDone(); | 251 _checkIfDone(); |
| 238 } | 252 } |
| 239 } | 253 } |
| 240 | 254 |
| 241 /** | 255 /** |
| 242 * Runs the function and catches uncaught errors. | 256 * Runs the function and catches uncaught errors. |
| 243 * | 257 * |
| 244 * Uncaught errors are given to [handleUncaughtError]. | 258 * Uncaught errors are given to [handleUncaughtError]. |
| 245 */ | 259 */ |
| 246 _runGuarded(void fun()) { | 260 _runGuarded(void fun()) { |
| 247 try { | 261 _runInZone(fun, true); |
| 248 _runInZone(fun); | 262 } |
| 249 } catch(e, s) { | 263 |
| 250 handleUncaughtError(_asyncError(e, s)); | 264 /** |
| 251 } | 265 * Runs the function but doesn't catch uncaught errors. |
| 266 */ | |
| 267 _runUnguarded(void fun()) { | |
| 268 _runInZone(fun, false); | |
| 252 } | 269 } |
| 253 | 270 |
| 254 runAsync(void fun()) { | 271 runAsync(void fun()) { |
| 255 _openCallbacks++; | 272 _openCallbacks++; |
| 256 _scheduleAsyncCallback(() { | 273 _scheduleAsyncCallback(() { |
| 257 _openCallbacks--; | 274 _openCallbacks--; |
| 258 _runGuarded(fun); | 275 _runGuarded(fun); |
| 259 }); | 276 }); |
| 260 } | 277 } |
| 261 | 278 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 var trace = getAttachedStackTrace(error); | 324 var trace = getAttachedStackTrace(error); |
| 308 _attachStackTrace(error, null); | 325 _attachStackTrace(error, null); |
| 309 if (trace != null) { | 326 if (trace != null) { |
| 310 print("Stack Trace:\n$trace\n"); | 327 print("Stack Trace:\n$trace\n"); |
| 311 } | 328 } |
| 312 throw error; | 329 throw error; |
| 313 }); | 330 }); |
| 314 } | 331 } |
| 315 } | 332 } |
| 316 | 333 |
| 334 typedef void _CompletionCallback(); | |
| 335 | |
| 317 /** | 336 /** |
| 318 * A zone that can execute a callback (through a future) when the zone is dead. | 337 * A zone that executes a callback when the zone is dead. |
| 319 */ | 338 */ |
| 320 class _WaitForCompletionZone extends _ZoneBase { | 339 class _WaitForCompletionZone extends _ZoneBase { |
| 321 final Completer _doneCompleter = new Completer(); | 340 final _CompletionCallback _onDone; |
| 322 | 341 |
| 323 _WaitForCompletionZone(_Zone parentZone) : super(parentZone); | 342 _WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone); |
| 324 | 343 |
| 325 /** | 344 /** |
| 326 * Runs the given function asynchronously and returns a future that is | 345 * Runs the given function asynchronously. Executes the [_onDone] callback |
| 327 * completed with `null` once the zone is done. | 346 * when the zone is done. |
| 328 */ | 347 */ |
| 329 Future runWaitForCompletion(void fun()) { | 348 void runWaitForCompletion(void fun()) { |
| 330 _runInZone(() { | 349 this._runGuarded(fun); |
| 331 try { | |
| 332 fun(); | |
| 333 } catch (e, s) { | |
| 334 handleUncaughtError(_asyncError(e, s)); | |
| 335 } | |
| 336 }); | |
| 337 return _doneCompleter.future; | |
| 338 } | 350 } |
| 339 | 351 |
| 340 _dispose() { | 352 _dispose() { |
| 341 super._dispose(); | 353 super._dispose(); |
| 342 _doneCompleter.complete(); | 354 _onDone(); |
| 343 } | 355 } |
| 344 | 356 |
| 345 String toString() => "WaitForCompletion ${super.toString()}"; | 357 String toString() => "WaitForCompletion ${super.toString()}"; |
| 346 } | 358 } |
| 347 | 359 |
| 360 typedef bool _HandleErrorCallback(error); | |
| 361 | |
| 348 /** | 362 /** |
| 349 * A zone that collects all uncaught errors and provides them in a stream. | 363 * A zone that collects all uncaught errors and provides them in a stream. |
| 350 * The stream is closed when the zone is done. | 364 * The stream is closed when the zone is done. |
| 351 */ | 365 */ |
| 352 class _CatchErrorsZone extends _WaitForCompletionZone { | 366 class _CatchErrorsZone extends _WaitForCompletionZone { |
| 353 final StreamController errorsController = new StreamController(); | 367 final _HandleErrorCallback _handleError; |
| 354 | 368 |
| 355 Stream get errors => errorsController.stream; | 369 _CatchErrorsZone(_Zone parentZone, this._handleError, void onDone()) |
| 356 | 370 : super(parentZone, onDone); |
| 357 _CatchErrorsZone(_Zone parentZone) : super(parentZone); | |
| 358 | 371 |
| 359 _Zone get _errorZone => this; | 372 _Zone get _errorZone => this; |
| 360 | 373 |
| 361 handleUncaughtError(error) { | 374 handleUncaughtError(error) { |
| 362 errorsController.add(error); | 375 if (!_handleError(error)) _parentZone.handleUncaughtError(error); |
| 363 } | |
| 364 | |
| 365 Future runWaitForCompletion(void fun()) { | |
| 366 super.runWaitForCompletion(fun).whenComplete(() { | |
| 367 errorsController.close(); | |
| 368 }); | |
| 369 } | 376 } |
| 370 | 377 |
| 371 String toString() => "WithErrors ${super.toString()}"; | 378 String toString() => "WithErrors ${super.toString()}"; |
| 372 } | 379 } |
| 373 | 380 |
| 374 typedef void _TimerCallback(); | 381 typedef void _TimerCallback(); |
| 375 | 382 |
| 376 /** | 383 /** |
| 377 * A [Timer] class that takes zones into account. | 384 * A [Timer] class that takes zones into account. |
| 378 */ | 385 */ |
| 379 class _ZoneTimer implements Timer { | 386 class _ZoneTimer implements Timer { |
| 380 final _Zone _zone; | 387 final _Zone _zone; |
| 381 final _TimerCallback _callback; | 388 final _TimerCallback _callback; |
| 382 Timer _timer; | 389 Timer _timer; |
| 383 bool _isDone = false; | 390 bool _isDone = false; |
| 384 | 391 |
| 385 _ZoneTimer(this._zone, Duration duration, this._callback) { | 392 _ZoneTimer(this._zone, Duration duration, this._callback) { |
| 386 _zone.expectCallback(); | 393 _zone.expectCallback(); |
| 387 _timer = _createTimer(duration, this.run); | 394 _timer = _createTimer(duration, this.run); |
| 388 } | 395 } |
| 389 | 396 |
| 390 void run() { | 397 void run() { |
| 391 _isDone = true; | 398 _isDone = true; |
| 392 _zone.executeCallbackGuarded(_callback); | 399 _zone.executeCallbackGuarded(_callback); |
| 393 } | 400 } |
| 394 | 401 |
| 395 void cancel() { | 402 void cancel() { |
| 396 if (!_isDone) _zone.unexpectCallback(); | 403 if (!_isDone) _zone.cancelCallbackExpectation(); |
| 397 _isDone = true; | 404 _isDone = true; |
| 398 _timer.cancel(); | 405 _timer.cancel(); |
| 399 } | 406 } |
| 400 } | 407 } |
| 401 | 408 |
| 402 typedef void _PeriodicTimerCallback(Timer timer); | 409 typedef void _PeriodicTimerCallback(Timer timer); |
| 403 | 410 |
| 404 /** | 411 /** |
| 405 * A [Timer] class for periodic callbacks that takes zones into account. | 412 * A [Timer] class for periodic callbacks that takes zones into account. |
| 406 */ | 413 */ |
| 407 class _PeriodicZoneTimer implements Timer { | 414 class _PeriodicZoneTimer implements Timer { |
| 408 final _Zone _zone; | 415 final _Zone _zone; |
| 409 final _PeriodicTimerCallback _callback; | 416 final _PeriodicTimerCallback _callback; |
| 410 Timer _timer; | 417 Timer _timer; |
| 411 bool _isDone = false; | 418 bool _isDone = false; |
| 412 | 419 |
| 413 _PeriodicZoneTimer(this._zone, Duration duration, this._callback) { | 420 _PeriodicZoneTimer(this._zone, Duration duration, this._callback) { |
| 414 _zone.expectCallback(); | 421 _zone.expectCallback(); |
| 415 _timer = _createPeriodicTimer(duration, this.run); | 422 _timer = _createPeriodicTimer(duration, this.run); |
| 416 } | 423 } |
| 417 | 424 |
| 418 void run(Timer timer) { | 425 void run(Timer timer) { |
| 419 assert(identical(_timer, timer)); | 426 assert(identical(_timer, timer)); |
| 420 _zone.executeGuardedPeriodicCallback(() { _callback(this); }); | 427 _zone.executePeriodicCallbackGuarded(() { _callback(this); }); |
| 421 } | 428 } |
| 422 | 429 |
| 423 void cancel() { | 430 void cancel() { |
| 424 if (!_isDone) _zone.unexpectCallback(); | 431 if (!_isDone) _zone.cancelCallbackExpectation(); |
| 425 _isDone = true; | 432 _isDone = true; |
| 426 _timer.cancel(); | 433 _timer.cancel(); |
| 427 } | 434 } |
| 428 } | 435 } |
| 429 | 436 |
| 430 Stream catchErrors(void body()) { | 437 Stream catchErrors(void body()) { |
| 431 _Zone catchErrorsZone = new _CatchErrorsZone(_Zone._current); | 438 _CatchErrorsZone catchErrorsZone; |
| 432 catchErrorsZone.runWaitForCompletion(body); | 439 StreamController controller; |
| 433 return catchErrorsZone.errors; | 440 |
| 441 void onListen() { | |
| 442 catchErrorsZone.runWaitForCompletion(body); | |
| 443 } | |
| 444 | |
| 445 bool handleError(e) { | |
| 446 controller.add(e); | |
| 447 return true; | |
| 448 } | |
| 449 | |
| 450 void onDone() { | |
| 451 controller.close(); | |
| 452 } | |
| 453 | |
| 454 catchErrorsZone = new _CatchErrorsZone(_Zone._current, handleError, onDone); | |
| 455 controller = new StreamController(onListen: onListen); | |
| 456 return controller.stream; | |
| 434 } | 457 } |
| 435 | 458 |
| 436 Future waitForCompletion(void body()) { | 459 Future waitForCompletion(void body()) { |
| 437 _Zone zone = new _WaitForCompletionZone(_Zone._current); | 460 Completer completer = new Completer.sync(); |
| 438 return zone.runWaitForCompletion(body); | 461 _Zone zone = new _WaitForCompletionZone(_Zone._current, completer.complete); |
| 462 zone.runWaitForCompletion(body); | |
| 463 return completer.future; | |
| 439 } | 464 } |
| OLD | NEW |