Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1517 lines (1366 sloc) 53.7 KB
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.async;
typedef R ZoneCallback<R>();
typedef R ZoneUnaryCallback<R, T>(T arg);
typedef R ZoneBinaryCallback<R, T1, T2>(T1 arg1, T2 arg2);
typedef HandleUncaughtErrorHandler = void Function(Zone self,
ZoneDelegate parent, Zone zone, Object error, StackTrace stackTrace);
typedef RunHandler = R Function<R>(
Zone self, ZoneDelegate parent, Zone zone, R Function() f);
typedef RunUnaryHandler = R Function<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f, T arg);
typedef RunBinaryHandler = R Function<R, T1, T2>(Zone self, ZoneDelegate parent,
Zone zone, R Function(T1 arg1, T2 arg2) f, T1 arg1, T2 arg2);
typedef RegisterCallbackHandler = ZoneCallback<R> Function<R>(
Zone self, ZoneDelegate parent, Zone zone, R Function() f);
typedef RegisterUnaryCallbackHandler = ZoneUnaryCallback<R, T> Function<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R Function(T arg) f);
typedef RegisterBinaryCallbackHandler
= ZoneBinaryCallback<R, T1, T2> Function<R, T1, T2>(Zone self,
ZoneDelegate parent, Zone zone, R Function(T1 arg1, T2 arg2) f);
typedef AsyncError ErrorCallbackHandler(Zone self, ZoneDelegate parent,
Zone zone, Object error, StackTrace stackTrace);
typedef void ScheduleMicrotaskHandler(
Zone self, ZoneDelegate parent, Zone zone, void f());
typedef Timer CreateTimerHandler(
Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
typedef Timer CreatePeriodicTimerHandler(Zone self, ZoneDelegate parent,
Zone zone, Duration period, void f(Timer timer));
typedef void PrintHandler(
Zone self, ZoneDelegate parent, Zone zone, String line);
typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification specification, Map zoneValues);
/** Pair of error and stack trace. Returned by [Zone.errorCallback]. */
class AsyncError implements Error {
final Object error;
final StackTrace stackTrace;
AsyncError(this.error, this.stackTrace);
String toString() => '$error';
}
class _ZoneFunction<T extends Function> {
final _Zone zone;
final T function;
const _ZoneFunction(this.zone, this.function);
}
/**
* This class provides the specification for a forked zone.
*
* When forking a new zone (see [Zone.fork]) one can override the default
* behavior of the zone by providing callbacks. These callbacks must be
* given in an instance of this class.
*
* Handlers have the same signature as the same-named methods on [Zone] but
* receive three additional arguments:
*
* 1. the zone the handlers are attached to (the "self" zone).
* 2. a [ZoneDelegate] to the parent zone.
* 3. the zone that first received the request (before the request was
* bubbled up).
*
* Handlers can either stop propagation the request (by simply not calling the
* parent handler), or forward to the parent zone, potentially modifying the
* arguments on the way.
*/
abstract class ZoneSpecification {
/**
* Creates a specification with the provided handlers.
*/
const factory ZoneSpecification(
{HandleUncaughtErrorHandler handleUncaughtError,
RunHandler run,
RunUnaryHandler runUnary,
RunBinaryHandler runBinary,
RegisterCallbackHandler registerCallback,
RegisterUnaryCallbackHandler registerUnaryCallback,
RegisterBinaryCallbackHandler registerBinaryCallback,
ErrorCallbackHandler errorCallback,
ScheduleMicrotaskHandler scheduleMicrotask,
CreateTimerHandler createTimer,
CreatePeriodicTimerHandler createPeriodicTimer,
PrintHandler print,
ForkHandler fork}) = _ZoneSpecification;
/**
* Creates a specification from [other] with the provided handlers overriding
* the ones in [other].
*/
factory ZoneSpecification.from(ZoneSpecification other,
{HandleUncaughtErrorHandler handleUncaughtError,
RunHandler run,
RunUnaryHandler runUnary,
RunBinaryHandler runBinary,
RegisterCallbackHandler registerCallback,
RegisterUnaryCallbackHandler registerUnaryCallback,
RegisterBinaryCallbackHandler registerBinaryCallback,
ErrorCallbackHandler errorCallback,
ScheduleMicrotaskHandler scheduleMicrotask,
CreateTimerHandler createTimer,
CreatePeriodicTimerHandler createPeriodicTimer,
PrintHandler print,
ForkHandler fork}) {
return new ZoneSpecification(
handleUncaughtError: handleUncaughtError ?? other.handleUncaughtError,
run: run ?? other.run,
runUnary: runUnary ?? other.runUnary,
runBinary: runBinary ?? other.runBinary,
registerCallback: registerCallback ?? other.registerCallback,
registerUnaryCallback:
registerUnaryCallback ?? other.registerUnaryCallback,
registerBinaryCallback:
registerBinaryCallback ?? other.registerBinaryCallback,
errorCallback: errorCallback ?? other.errorCallback,
scheduleMicrotask: scheduleMicrotask ?? other.scheduleMicrotask,
createTimer: createTimer ?? other.createTimer,
createPeriodicTimer: createPeriodicTimer ?? other.createPeriodicTimer,
print: print ?? other.print,
fork: fork ?? other.fork);
}
HandleUncaughtErrorHandler get handleUncaughtError;
RunHandler get run;
RunUnaryHandler get runUnary;
RunBinaryHandler get runBinary;
RegisterCallbackHandler get registerCallback;
RegisterUnaryCallbackHandler get registerUnaryCallback;
RegisterBinaryCallbackHandler get registerBinaryCallback;
ErrorCallbackHandler get errorCallback;
ScheduleMicrotaskHandler get scheduleMicrotask;
CreateTimerHandler get createTimer;
CreatePeriodicTimerHandler get createPeriodicTimer;
PrintHandler get print;
ForkHandler get fork;
}
/**
* Internal [ZoneSpecification] class.
*
* The implementation wants to rely on the fact that the getters cannot change
* dynamically. We thus require users to go through the redirecting
* [ZoneSpecification] constructor which instantiates this class.
*/
class _ZoneSpecification implements ZoneSpecification {
const _ZoneSpecification(
{this.handleUncaughtError,
this.run,
this.runUnary,
this.runBinary,
this.registerCallback,
this.registerUnaryCallback,
this.registerBinaryCallback,
this.errorCallback,
this.scheduleMicrotask,
this.createTimer,
this.createPeriodicTimer,
this.print,
this.fork});
final HandleUncaughtErrorHandler handleUncaughtError;
final RunHandler run;
final RunUnaryHandler runUnary;
final RunBinaryHandler runBinary;
final RegisterCallbackHandler registerCallback;
final RegisterUnaryCallbackHandler registerUnaryCallback;
final RegisterBinaryCallbackHandler registerBinaryCallback;
final ErrorCallbackHandler errorCallback;
final ScheduleMicrotaskHandler scheduleMicrotask;
final CreateTimerHandler createTimer;
final CreatePeriodicTimerHandler createPeriodicTimer;
final PrintHandler print;
final ForkHandler fork;
}
/**
* An adapted view of the parent zone.
*
* This class allows the implementation of a zone method to invoke methods on
* the parent zone while retaining knowledge of the originating zone.
*
* Custom zones (created through [Zone.fork] or [runZoned]) can provide
* implementations of most methods of zones. This is similar to overriding
* methods on [Zone], except that this mechanism doesn't require subclassing.
*
* A custom zone function (provided through a [ZoneSpecification]) typically
* records or wraps its parameters and then delegates the operation to its
* parent zone using the provided [ZoneDelegate].
*
* While zones have access to their parent zone (through [Zone.parent]) it is
* recommended to call the methods on the provided parent delegate for two
* reasons:
* 1. the delegate methods take an additional `zone` argument which is the
* zone the action has been initiated in.
* 2. delegate calls are more efficient, since the implementation knows how
* to skip zones that would just delegate to their parents.
*/
abstract class ZoneDelegate {
void handleUncaughtError(Zone zone, error, StackTrace stackTrace);
R run<R>(Zone zone, R f());
R runUnary<R, T>(Zone zone, R f(T arg), T arg);
R runBinary<R, T1, T2>(Zone zone, R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2);
ZoneCallback<R> registerCallback<R>(Zone zone, R f());
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(Zone zone, R f(T arg));
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
Zone zone, R f(T1 arg1, T2 arg2));
AsyncError errorCallback(Zone zone, Object error, StackTrace stackTrace);
void scheduleMicrotask(Zone zone, void f());
Timer createTimer(Zone zone, Duration duration, void f());
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
void print(Zone zone, String line);
Zone fork(Zone zone, ZoneSpecification specification, Map zoneValues);
}
/**
* A zone represents an environment that remains stable across asynchronous
* calls.
*
* Code is always executed in the context of a zone, available as
* [Zone.current]. The initial `main` function runs in the context of the
* default zone ([Zone.root]). Code can be run in a different zone using either
* [runZoned], to create a new zone, or [Zone.run] to run code in the context of
* an existing zone likely created using [Zone.fork].
*
* Developers can create a new zone that overrides some of the functionality of
* an existing zone. For example, custom zones can replace of modify the
* behavior of `print`, timers, microtasks or how uncaught errors are handled.
*
* The [Zone] class is not subclassable, but users can provide custom zones by
* forking an existing zone (usually [Zone.current]) with a [ZoneSpecification].
* This is similar to creating a new class that extends the base `Zone` class
* and that overrides some methods, except without actually creating a new
* class. Instead the overriding methods are provided as functions that
* explicitly take the equivalent of their own class, the "super" class and the
* `this` object as parameters.
*
* Asynchronous callbacks always run in the context of the zone where they were
* scheduled. This is implemented using two steps:
* 1. the callback is first registered using one of [registerCallback],
* [registerUnaryCallback], or [registerBinaryCallback]. This allows the zone
* to record that a callback exists and potentially modify it (by returning a
* different callback). The code doing the registration (e.g., `Future.then`)
* also remembers the current zone so that it can later run the callback in
* that zone.
* 2. At a later point the registered callback is run in the remembered zone.
*
* This is all handled internally by the platform code and most users don't need
* to worry about it. However, developers of new asynchronous operations,
* provided by the underlying system or through native extensions, must follow
* the protocol to be zone compatible.
*
* For convenience, zones provide [bindCallback] (and the corresponding
* [bindUnaryCallback] and [bindBinaryCallback]) to make it easier to respect
* the zone contract: these functions first invoke the corresponding `register`
* functions and then wrap the returned function so that it runs in the current
* zone when it is later asynchronously invoked.
*
* Similarly, zones provide [bindCallbackGuarded] (and the corresponding
* [bindUnaryCallbackGuarded] and [bindBinaryCallbackGuarded]), when the
* callback should be invoked through [Zone.runGuarded].
*/
abstract class Zone {
// Private constructor so that it is not possible instantiate a Zone class.
Zone._();
/**
* The root zone.
*
* All isolate entry functions (`main` or spawned functions) start running in
* the root zone (that is, [Zone.current] is identical to [Zone.root] when the
* entry function is called). If no custom zone is created, the rest of the
* program always runs in the root zone.
*
* The root zone implements the default behavior of all zone operations.
* Many methods, like [registerCallback] do the bare minimum required of the
* function, and are only provided as a hook for custom zones. Others, like
* [scheduleMicrotask], interact with the underlying system to implement the
* desired behavior.
*/
static const Zone root = _rootZone;
/** The currently running zone. */
static Zone _current = _rootZone;
/** The zone that is currently active. */
static Zone get current => _current;
/**
* Handles uncaught asynchronous errors.
*
* There are two kind of asynchronous errors that are handled by this
* function:
* 1. Uncaught errors that were thrown in asynchronous callbacks, for example,
* a `throw` in the function passed to [Timer.run].
* 2. Asynchronous errors that are pushed through [Future] and [Stream]
* chains, but for which no child registered an error handler.
* Most asynchronous classes, like [Future] or [Stream] push errors to their
* listeners. Errors are propagated this way until either a listener handles
* the error (for example with [Future.catchError]), or no listener is
* available anymore. In the latter case, futures and streams invoke the
* zone's [handleUncaughtError].
*
* By default, when handled by the root zone, uncaught asynchronous errors are
* treated like uncaught synchronous exceptions.
*/
void handleUncaughtError(error, StackTrace stackTrace);
/**
* The parent zone of the this zone.
*
* Is `null` if `this` is the [root] zone.
*
* Zones are created by [fork] on an existing zone, or by [runZoned] which
* forks the [current] zone. The new zone's parent zone is the zone it was
* forked from.
*/
Zone get parent;
/**
* The error zone is the one that is responsible for dealing with uncaught
* errors.
*
* This is the closest parent zone of this zone that provides a
* [handleUncaughtError] method.
*
* Asynchronous errors never cross zone boundaries between zones with
* different error handlers.
*
* Example:
* ```
* import 'dart:async';
*
* main() {
* var future;
* runZoned(() {
* // The asynchronous error is caught by the custom zone which prints
* // 'asynchronous error'.
* future = new Future.error("asynchronous error");
* }, onError: (e) { print(e); }); // Creates a zone with an error handler.
* // The following `catchError` handler is never invoked, because the
* // custom zone created by the call to `runZoned` provides an
* // error handler.
* future.catchError((e) { throw "is never reached"; });
* }
* ```
*
* Note that errors cannot enter a child zone with a different error handler
* either:
* ```
* import 'dart:async';
*
* main() {
* runZoned(() {
* // The following asynchronous error is *not* caught by the `catchError`
* // in the nested zone, since errors are not to cross zone boundaries
* // with different error handlers.
* // Instead the error is handled by the current error handler,
* // printing "Caught by outer zone: asynchronous error".
* var future = new Future.error("asynchronous error");
* runZoned(() {
* future.catchError((e) { throw "is never reached"; });
* }, onError: (e) { throw "is never reached"; });
* }, onError: (e) { print("Caught by outer zone: $e"); });
* }
* ```
*/
Zone get errorZone;
/**
* Returns true if `this` and [otherZone] are in the same error zone.
*
* Two zones are in the same error zone if they have the same [errorZone].
*/
bool inSameErrorZone(Zone otherZone);
/**
* Creates a new zone as a child of `this`.
*
* The new zone uses the closures in the given [specification] to override
* the current's zone behavior. All specification entries that are `null`
* inherit the behavior from the parent zone (`this`).
*
* The new zone inherits the stored values (accessed through [operator []])
* of this zone and updates them with values from [zoneValues], which either
* adds new values or overrides existing ones.
*
* Note that the fork operation is interceptible. A zone can thus change
* the zone specification (or zone values), giving the forking zone full
* control over the child zone.
*/
Zone fork({ZoneSpecification specification, Map zoneValues});
/**
* Executes [action] in this zone.
*
* By default (as implemented in the [root] zone), runs [action]
* with [current] set to this zone.
*
* If [action] throws, the synchronous exception is not caught by the zone's
* error handler. Use [runGuarded] to achieve that.
*
* Since the root zone is the only zone that can modify the value of
* [current], custom zones intercepting run should always delegate to their
* parent zone. They may take actions before and after the call.
*/
R run<R>(R action());
/**
* Executes the given [action] with [argument] in this zone.
*
* As [run] except that [action] is called with one [argument] instead of
* none.
*/
R runUnary<R, T>(R action(T argument), T argument);
/**
* Executes the given [action] with [argument1] and [argument2] in this
* zone.
*
* As [run] except that [action] is called with two arguments instead of none.
*/
R runBinary<R, T1, T2>(
R action(T1 argument1, T2 argument2), T1 argument1, T2 argument2);
/**
* Executes the given [action] in this zone and catches synchronous
* errors.
*
* This function is equivalent to:
* ```
* try {
* this.run(action);
* } catch (e, s) {
* this.handleUncaughtError(e, s);
* }
* ```
*
* See [run].
*/
void runGuarded(void action());
/**
* Executes the given [action] with [argument] in this zone and
* catches synchronous errors.
*
* See [runGuarded].
*/
void runUnaryGuarded<T>(void action(T argument), T argument);
/**
* Executes the given [action] with [argument1] and [argument2] in this
* zone and catches synchronous errors.
*
* See [runGuarded].
*/
void runBinaryGuarded<T1, T2>(
void action(T1 argument1, T2 argument2), T1 argument1, T2 argument2);
/**
* Registers the given callback in this zone.
*
* When implementing an asynchronous primitive that uses callbacks, the
* callback must be registered using [registerCallback] at the point where the
* user provides the callback. This allows zones to record other information
* that they need at the same time, perhaps even wrapping the callback, so
* that the callback is prepared when it is later run in the same zones
* (using [run]). For example, a zone may decide
* to store the stack trace (at the time of the registration) with the
* callback.
*
* Returns the callback that should be used in place of the provided
* [callback]. Frequently zones simply return the original callback.
*
* Custom zones may intercept this operation. The default implementation in
* [Zone.root] returns the original callback unchanged.
*/
ZoneCallback<R> registerCallback<R>(R callback());
/**
* Registers the given callback in this zone.
*
* Similar to [registerCallback] but with a unary callback.
*/
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg));
/**
* Registers the given callback in this zone.
*
* Similar to [registerCallback] but with a unary callback.
*/
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
R callback(T1 arg1, T2 arg2));
/**
* Registers the provided [callback] and returns a function that will
* execute in this zone.
*
* Equivalent to:
*
* ZoneCallback registered = this.registerCallback(callback);
* return () => this.run(registered);
*
*/
ZoneCallback<R> bindCallback<R>(R callback());
/**
* Registers the provided [callback] and returns a function that will
* execute in this zone.
*
* Equivalent to:
*
* ZoneCallback registered = this.registerUnaryCallback(callback);
* return (arg) => thin.runUnary(registered, arg);
*/
ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R callback(T argument));
/**
* Registers the provided [callback] and returns a function that will
* execute in this zone.
*
* Equivalent to:
*
* ZoneCallback registered = registerBinaryCallback(callback);
* return (arg1, arg2) => thin.runBinary(registered, arg1, arg2);
*/
ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
R callback(T1 argument1, T2 argument2));
/**
* Registers the provided [callback] and returns a function that will
* execute in this zone.
*
* When the function executes, errors are caught and treated as uncaught
* errors.
*
* Equivalent to:
*
* ZoneCallback registered = this.registerCallback(callback);
* return () => this.runGuarded(registered);
*
*/
void Function() bindCallbackGuarded(void callback());
/**
* Registers the provided [callback] and returns a function that will
* execute in this zone.
*
* When the function executes, errors are caught and treated as uncaught
* errors.
*
* Equivalent to:
*
* ZoneCallback registered = this.registerUnaryCallback(callback);
* return (arg) => this.runUnaryGuarded(registered, arg);
*/
void Function(T) bindUnaryCallbackGuarded<T>(void callback(T argument));
/**
* Registers the provided [callback] and returns a function that will
* execute in this zone.
*
* Equivalent to:
*
* ZoneCallback registered = registerBinaryCallback(callback);
* return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2);
*/
void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
void callback(T1 argument1, T2 argument2));
/**
* Intercepts errors when added programmatically to a `Future` or `Stream`.
*
* When calling [Completer.completeError], [StreamController.addError],
* or some [Future] constructors, the current zone is allowed to intercept
* and replace the error.
*
* Future constructors invoke this function when the error is received
* directly, for example with [Future.error], or when the error is caught
* synchronously, for example with [Future.sync].
*
* There is no guarantee that an error is only sent through [errorCallback]
* once. Libraries that use intermediate controllers or completers might
* end up invoking [errorCallback] multiple times.
*
* Returns `null` if no replacement is desired. Otherwise returns an instance
* of [AsyncError] holding the new pair of error and stack trace.
*
* Although not recommended, the returned instance may have its `error` member
* ([AsyncError.error]) be equal to `null` in which case the error should be
* replaced by a [NullThrownError].
*
* Custom zones may intercept this operation.
*
* Implementations of a new asynchronous primitive that converts synchronous
* errors to asynchronous errors rarely need to invoke [errorCallback], since
* errors are usually reported through future completers or stream
* controllers.
*/
AsyncError errorCallback(Object error, StackTrace stackTrace);
/**
* Runs [callback] asynchronously in this zone.
*
* The global `scheduleMicrotask` delegates to the current zone's
* [scheduleMicrotask]. The root zone's implementation interacts with the
* underlying system to schedule the given callback as a microtask.
*
* Custom zones may intercept this operation (for example to wrap the given
* [callback]).
*/
void scheduleMicrotask(void callback());
/**
* Creates a Timer where the callback is executed in this zone.
*/
Timer createTimer(Duration duration, void callback());
/**
* Creates a periodic Timer where the callback is executed in this zone.
*/
Timer createPeriodicTimer(Duration period, void callback(Timer timer));
/**
* Prints the given [line].
*
* The global `print` function delegates to the current zone's [print]
* function which makes it possible to intercept printing.
*
* Example:
* ```
* import 'dart:async';
*
* main() {
* runZoned(() {
* // Ends up printing: "Intercepted: in zone".
* print("in zone");
* }, zoneSpecification: new ZoneSpecification(
* print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
* parent.print(zone, "Intercepted: $line");
* }));
* }
* ```
*/
void print(String line);
/**
* Call to enter the Zone.
*
* The previous current zone is returned.
*/
static Zone _enter(Zone zone) {
assert(zone != null);
assert(!identical(zone, _current));
Zone previous = _current;
_current = zone;
return previous;
}
/**
* Call to leave the Zone.
*
* The previous Zone must be provided as `previous`.
*/
static void _leave(Zone previous) {
assert(previous != null);
Zone._current = previous;
}
/**
* Retrieves the zone-value associated with [key].
*
* If this zone does not contain the value looks up the same key in the
* parent zone. If the [key] is not found returns `null`.
*
* Any object can be used as key, as long as it has compatible `operator ==`
* and `hashCode` implementations.
* By controlling access to the key, a zone can grant or deny access to the
* zone value.
*/
operator [](Object key);
}
ZoneDelegate _parentDelegate(_Zone zone) {
if (zone.parent == null) return null;
return zone.parent._delegate;
}
class _ZoneDelegate implements ZoneDelegate {
final _Zone _delegationTarget;
_ZoneDelegate(this._delegationTarget);
void handleUncaughtError(Zone zone, error, StackTrace stackTrace) {
var implementation = _delegationTarget._handleUncaughtError;
_Zone implZone = implementation.zone;
HandleUncaughtErrorHandler handler = implementation.function;
return handler(
implZone, _parentDelegate(implZone), zone, error, stackTrace);
}
R run<R>(Zone zone, R f()) {
var implementation = _delegationTarget._run;
_Zone implZone = implementation.zone;
RunHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, f);
}
R runUnary<R, T>(Zone zone, R f(T arg), T arg) {
var implementation = _delegationTarget._runUnary;
_Zone implZone = implementation.zone;
RunUnaryHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, f, arg);
}
R runBinary<R, T1, T2>(Zone zone, R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
var implementation = _delegationTarget._runBinary;
_Zone implZone = implementation.zone;
RunBinaryHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, f, arg1, arg2);
}
ZoneCallback<R> registerCallback<R>(Zone zone, R f()) {
var implementation = _delegationTarget._registerCallback;
_Zone implZone = implementation.zone;
RegisterCallbackHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, f);
}
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(Zone zone, R f(T arg)) {
var implementation = _delegationTarget._registerUnaryCallback;
_Zone implZone = implementation.zone;
RegisterUnaryCallbackHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, f);
}
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
Zone zone, R f(T1 arg1, T2 arg2)) {
var implementation = _delegationTarget._registerBinaryCallback;
_Zone implZone = implementation.zone;
RegisterBinaryCallbackHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, f);
}
AsyncError errorCallback(Zone zone, Object error, StackTrace stackTrace) {
var implementation = _delegationTarget._errorCallback;
_Zone implZone = implementation.zone;
if (identical(implZone, _rootZone)) return null;
ErrorCallbackHandler handler = implementation.function;
return handler(
implZone, _parentDelegate(implZone), zone, error, stackTrace);
}
void scheduleMicrotask(Zone zone, f()) {
var implementation = _delegationTarget._scheduleMicrotask;
_Zone implZone = implementation.zone;
ScheduleMicrotaskHandler handler = implementation.function;
handler(implZone, _parentDelegate(implZone), zone, f);
}
Timer createTimer(Zone zone, Duration duration, void f()) {
var implementation = _delegationTarget._createTimer;
_Zone implZone = implementation.zone;
CreateTimerHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, duration, f);
}
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
var implementation = _delegationTarget._createPeriodicTimer;
_Zone implZone = implementation.zone;
CreatePeriodicTimerHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, period, f);
}
void print(Zone zone, String line) {
var implementation = _delegationTarget._print;
_Zone implZone = implementation.zone;
PrintHandler handler = implementation.function;
handler(implZone, _parentDelegate(implZone), zone, line);
}
Zone fork(Zone zone, ZoneSpecification specification, Map zoneValues) {
var implementation = _delegationTarget._fork;
_Zone implZone = implementation.zone;
ForkHandler handler = implementation.function;
return handler(
implZone, _parentDelegate(implZone), zone, specification, zoneValues);
}
}
/**
* Base class for Zone implementations.
*/
abstract class _Zone implements Zone {
const _Zone();
// TODO(floitsch): the types of the `_ZoneFunction`s should have a type for
// all fields.
_ZoneFunction<Function> get _run;
_ZoneFunction<Function> get _runUnary;
_ZoneFunction<Function> get _runBinary;
_ZoneFunction<Function> get _registerCallback;
_ZoneFunction<Function> get _registerUnaryCallback;
_ZoneFunction<Function> get _registerBinaryCallback;
_ZoneFunction<ErrorCallbackHandler> get _errorCallback;
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask;
_ZoneFunction<CreateTimerHandler> get _createTimer;
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer;
_ZoneFunction<PrintHandler> get _print;
_ZoneFunction<ForkHandler> get _fork;
_ZoneFunction<HandleUncaughtErrorHandler> get _handleUncaughtError;
_Zone get parent;
ZoneDelegate get _delegate;
Map get _map;
bool inSameErrorZone(Zone otherZone) {
return identical(this, otherZone) ||
identical(errorZone, otherZone.errorZone);
}
}
class _CustomZone extends _Zone {
// The actual zone and implementation of each of these
// inheritable zone functions.
// TODO(floitsch): the types of the `_ZoneFunction`s should have a type for
// all fields.
_ZoneFunction<Function> _run;
_ZoneFunction<Function> _runUnary;
_ZoneFunction<Function> _runBinary;
_ZoneFunction<Function> _registerCallback;
_ZoneFunction<Function> _registerUnaryCallback;
_ZoneFunction<Function> _registerBinaryCallback;
_ZoneFunction<ErrorCallbackHandler> _errorCallback;
_ZoneFunction<ScheduleMicrotaskHandler> _scheduleMicrotask;
_ZoneFunction<CreateTimerHandler> _createTimer;
_ZoneFunction<CreatePeriodicTimerHandler> _createPeriodicTimer;
_ZoneFunction<PrintHandler> _print;
_ZoneFunction<ForkHandler> _fork;
_ZoneFunction<HandleUncaughtErrorHandler> _handleUncaughtError;
// A cached delegate to this zone.
ZoneDelegate _delegateCache;
/// The parent zone.
final _Zone parent;
/// The zone's scoped value declaration map.
///
/// This is always a [HashMap].
final Map _map;
ZoneDelegate get _delegate {
if (_delegateCache != null) return _delegateCache;
_delegateCache = new _ZoneDelegate(this);
return _delegateCache;
}
_CustomZone(this.parent, ZoneSpecification specification, this._map) {
// The root zone will have implementations of all parts of the
// specification, so it will never try to access the (null) parent.
// All other zones have a non-null parent.
_run = (specification.run != null)
? new _ZoneFunction<Function>(this, specification.run)
: parent._run;
_runUnary = (specification.runUnary != null)
? new _ZoneFunction<Function>(this, specification.runUnary)
: parent._runUnary;
_runBinary = (specification.runBinary != null)
? new _ZoneFunction<Function>(this, specification.runBinary)
: parent._runBinary;
_registerCallback = (specification.registerCallback != null)
? new _ZoneFunction<Function>(this, specification.registerCallback)
: parent._registerCallback;
_registerUnaryCallback = (specification.registerUnaryCallback != null)
? new _ZoneFunction<Function>(this, specification.registerUnaryCallback)
: parent._registerUnaryCallback;
_registerBinaryCallback = (specification.registerBinaryCallback != null)
? new _ZoneFunction<Function>(
this, specification.registerBinaryCallback)
: parent._registerBinaryCallback;
_errorCallback = (specification.errorCallback != null)
? new _ZoneFunction<ErrorCallbackHandler>(
this, specification.errorCallback)
: parent._errorCallback;
_scheduleMicrotask = (specification.scheduleMicrotask != null)
? new _ZoneFunction<ScheduleMicrotaskHandler>(
this, specification.scheduleMicrotask)
: parent._scheduleMicrotask;
_createTimer = (specification.createTimer != null)
? new _ZoneFunction<CreateTimerHandler>(this, specification.createTimer)
: parent._createTimer;
_createPeriodicTimer = (specification.createPeriodicTimer != null)
? new _ZoneFunction<CreatePeriodicTimerHandler>(
this, specification.createPeriodicTimer)
: parent._createPeriodicTimer;
_print = (specification.print != null)
? new _ZoneFunction<PrintHandler>(this, specification.print)
: parent._print;
_fork = (specification.fork != null)
? new _ZoneFunction<ForkHandler>(this, specification.fork)
: parent._fork;
_handleUncaughtError = (specification.handleUncaughtError != null)
? new _ZoneFunction<HandleUncaughtErrorHandler>(
this, specification.handleUncaughtError)
: parent._handleUncaughtError;
}
/**
* The closest error-handling zone.
*
* Returns `this` if `this` has an error-handler. Otherwise returns the
* parent's error-zone.
*/
Zone get errorZone => _handleUncaughtError.zone;
void runGuarded(void f()) {
try {
run(f);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runUnaryGuarded<T>(void f(T arg), T arg) {
try {
runUnary(f, arg);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runBinaryGuarded<T1, T2>(void f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
try {
runBinary(f, arg1, arg2);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
ZoneCallback<R> bindCallback<R>(R f()) {
var registered = registerCallback(f);
return () => this.run(registered);
}
ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R f(T arg)) {
var registered = registerUnaryCallback(f);
return (arg) => this.runUnary(registered, arg);
}
ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
R f(T1 arg1, T2 arg2)) {
var registered = registerBinaryCallback(f);
return (arg1, arg2) => this.runBinary(registered, arg1, arg2);
}
void Function() bindCallbackGuarded(void f()) {
var registered = registerCallback(f);
return () => this.runGuarded(registered);
}
void Function(T) bindUnaryCallbackGuarded<T>(void f(T arg)) {
var registered = registerUnaryCallback(f);
return (arg) => this.runUnaryGuarded(registered, arg);
}
void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
void f(T1 arg1, T2 arg2)) {
var registered = registerBinaryCallback(f);
return (arg1, arg2) => this.runBinaryGuarded(registered, arg1, arg2);
}
operator [](Object key) {
var result = _map[key];
if (result != null || _map.containsKey(key)) return result;
// If we are not the root zone, look up in the parent zone.
if (parent != null) {
// We do not optimize for repeatedly looking up a key which isn't
// there. That would require storing the key and keeping it alive.
// Copying the key/value from the parent does not keep any new values
// alive.
var value = parent[key];
if (value != null) {
_map[key] = value;
}
return value;
}
assert(this == _rootZone);
return null;
}
// Methods that can be customized by the zone specification.
void handleUncaughtError(error, StackTrace stackTrace) {
var implementation = this._handleUncaughtError;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
HandleUncaughtErrorHandler handler = implementation.function;
return handler(
implementation.zone, parentDelegate, this, error, stackTrace);
}
Zone fork({ZoneSpecification specification, Map zoneValues}) {
var implementation = this._fork;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
ForkHandler handler = implementation.function;
return handler(
implementation.zone, parentDelegate, this, specification, zoneValues);
}
R run<R>(R f()) {
var implementation = this._run;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
RunHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, f);
}
R runUnary<R, T>(R f(T arg), T arg) {
var implementation = this._runUnary;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
RunUnaryHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, f, arg);
}
R runBinary<R, T1, T2>(R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
var implementation = this._runBinary;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
RunBinaryHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, f, arg1, arg2);
}
ZoneCallback<R> registerCallback<R>(R callback()) {
var implementation = this._registerCallback;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
RegisterCallbackHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, callback);
}
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R callback(T arg)) {
var implementation = this._registerUnaryCallback;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
RegisterUnaryCallbackHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, callback);
}
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
R callback(T1 arg1, T2 arg2)) {
var implementation = this._registerBinaryCallback;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
RegisterBinaryCallbackHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, callback);
}
AsyncError errorCallback(Object error, StackTrace stackTrace) {
var implementation = this._errorCallback;
assert(implementation != null);
final Zone implementationZone = implementation.zone;
if (identical(implementationZone, _rootZone)) return null;
final ZoneDelegate parentDelegate = _parentDelegate(implementationZone);
ErrorCallbackHandler handler = implementation.function;
return handler(implementationZone, parentDelegate, this, error, stackTrace);
}
void scheduleMicrotask(void f()) {
var implementation = this._scheduleMicrotask;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
ScheduleMicrotaskHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, f);
}
Timer createTimer(Duration duration, void f()) {
var implementation = this._createTimer;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
CreateTimerHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, duration, f);
}
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
var implementation = this._createPeriodicTimer;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
CreatePeriodicTimerHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, duration, f);
}
void print(String line) {
var implementation = this._print;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
PrintHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, line);
}
}
void _rootHandleUncaughtError(
Zone self, ZoneDelegate parent, Zone zone, error, StackTrace stackTrace) {
_schedulePriorityAsyncCallback(() {
error ??= new NullThrownError();
if (stackTrace == null) throw error;
_rethrow(error, stackTrace);
});
}
external void _rethrow(Object error, StackTrace stackTrace);
R _rootRun<R>(Zone self, ZoneDelegate parent, Zone zone, R f()) {
if (Zone._current == zone) return f();
Zone old = Zone._enter(zone);
try {
return f();
} finally {
Zone._leave(old);
}
}
R _rootRunUnary<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R f(T arg), T arg) {
if (Zone._current == zone) return f(arg);
Zone old = Zone._enter(zone);
try {
return f(arg);
} finally {
Zone._leave(old);
}
}
R _rootRunBinary<R, T1, T2>(Zone self, ZoneDelegate parent, Zone zone,
R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
if (Zone._current == zone) return f(arg1, arg2);
Zone old = Zone._enter(zone);
try {
return f(arg1, arg2);
} finally {
Zone._leave(old);
}
}
ZoneCallback<R> _rootRegisterCallback<R>(
Zone self, ZoneDelegate parent, Zone zone, R f()) {
return f;
}
ZoneUnaryCallback<R, T> _rootRegisterUnaryCallback<R, T>(
Zone self, ZoneDelegate parent, Zone zone, R f(T arg)) {
return f;
}
ZoneBinaryCallback<R, T1, T2> _rootRegisterBinaryCallback<R, T1, T2>(
Zone self, ZoneDelegate parent, Zone zone, R f(T1 arg1, T2 arg2)) {
return f;
}
AsyncError _rootErrorCallback(Zone self, ZoneDelegate parent, Zone zone,
Object error, StackTrace stackTrace) =>
null;
void _rootScheduleMicrotask(
Zone self, ZoneDelegate parent, Zone zone, void f()) {
if (!identical(_rootZone, zone)) {
bool hasErrorHandler = !_rootZone.inSameErrorZone(zone);
if (hasErrorHandler) {
f = zone.bindCallbackGuarded(f);
} else {
f = zone.bindCallback(f);
}
// Use root zone as event zone if the function is already bound.
zone = _rootZone;
}
_scheduleAsyncCallback(f);
}
Timer _rootCreateTimer(Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void callback()) {
if (!identical(_rootZone, zone)) {
callback = zone.bindCallback(callback);
}
return Timer._createTimer(duration, callback);
}
Timer _rootCreatePeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void callback(Timer timer)) {
if (!identical(_rootZone, zone)) {
// TODO(floitsch): the return type should be 'void'.
callback = zone.bindUnaryCallback<dynamic, Timer>(callback);
}
return Timer._createPeriodicTimer(duration, callback);
}
void _rootPrint(Zone self, ZoneDelegate parent, Zone zone, String line) {
printToConsole(line);
}
void _printToZone(String line) {
Zone.current.print(line);
}
Zone _rootFork(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification specification, Map zoneValues) {
// TODO(floitsch): it would be nice if we could get rid of this hack.
// Change the static zoneOrDirectPrint function to go through zones
// from now on.
printToZone = _printToZone;
if (specification == null) {
specification = const ZoneSpecification();
} else if (specification is! _ZoneSpecification) {
throw new ArgumentError("ZoneSpecifications must be instantiated"
" with the provided constructor.");
}
Map valueMap;
if (zoneValues == null) {
if (zone is _Zone) {
valueMap = zone._map;
} else {
valueMap = new HashMap();
}
} else {
valueMap = new HashMap.from(zoneValues);
}
return new _CustomZone(zone, specification, valueMap);
}
class _RootZone extends _Zone {
const _RootZone();
_ZoneFunction<Function> get _run =>
const _ZoneFunction<Function>(_rootZone, _rootRun);
_ZoneFunction<Function> get _runUnary =>
const _ZoneFunction<Function>(_rootZone, _rootRunUnary);
_ZoneFunction<Function> get _runBinary =>
const _ZoneFunction<Function>(_rootZone, _rootRunBinary);
_ZoneFunction<Function> get _registerCallback =>
const _ZoneFunction<Function>(_rootZone, _rootRegisterCallback);
_ZoneFunction<Function> get _registerUnaryCallback =>
const _ZoneFunction<Function>(_rootZone, _rootRegisterUnaryCallback);
_ZoneFunction<Function> get _registerBinaryCallback =>
const _ZoneFunction<Function>(_rootZone, _rootRegisterBinaryCallback);
_ZoneFunction<ErrorCallbackHandler> get _errorCallback =>
const _ZoneFunction<ErrorCallbackHandler>(_rootZone, _rootErrorCallback);
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask =>
const _ZoneFunction<ScheduleMicrotaskHandler>(
_rootZone, _rootScheduleMicrotask);
_ZoneFunction<CreateTimerHandler> get _createTimer =>
const _ZoneFunction<CreateTimerHandler>(_rootZone, _rootCreateTimer);
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer =>
const _ZoneFunction<CreatePeriodicTimerHandler>(
_rootZone, _rootCreatePeriodicTimer);
_ZoneFunction<PrintHandler> get _print =>
const _ZoneFunction<PrintHandler>(_rootZone, _rootPrint);
_ZoneFunction<ForkHandler> get _fork =>
const _ZoneFunction<ForkHandler>(_rootZone, _rootFork);
_ZoneFunction<HandleUncaughtErrorHandler> get _handleUncaughtError =>
const _ZoneFunction<HandleUncaughtErrorHandler>(
_rootZone, _rootHandleUncaughtError);
// The parent zone.
_Zone get parent => null;
/// The zone's scoped value declaration map.
///
/// This is always a [HashMap].
Map get _map => _rootMap;
static final _rootMap = new HashMap();
static ZoneDelegate _rootDelegate;
ZoneDelegate get _delegate {
if (_rootDelegate != null) return _rootDelegate;
return _rootDelegate = new _ZoneDelegate(this);
}
/**
* The closest error-handling zone.
*
* Returns `this` if `this` has an error-handler. Otherwise returns the
* parent's error-zone.
*/
Zone get errorZone => this;
// Zone interface.
void runGuarded(void f()) {
try {
if (identical(_rootZone, Zone._current)) {
f();
return;
}
_rootRun(null, null, this, f);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runUnaryGuarded<T>(void f(T arg), T arg) {
try {
if (identical(_rootZone, Zone._current)) {
f(arg);
return;
}
_rootRunUnary(null, null, this, f, arg);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
void runBinaryGuarded<T1, T2>(void f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
try {
if (identical(_rootZone, Zone._current)) {
f(arg1, arg2);
return;
}
_rootRunBinary(null, null, this, f, arg1, arg2);
} catch (e, s) {
handleUncaughtError(e, s);
}
}
ZoneCallback<R> bindCallback<R>(R f()) {
return () => this.run<R>(f);
}
ZoneUnaryCallback<R, T> bindUnaryCallback<R, T>(R f(T arg)) {
return (arg) => this.runUnary<R, T>(f, arg);
}
ZoneBinaryCallback<R, T1, T2> bindBinaryCallback<R, T1, T2>(
R f(T1 arg1, T2 arg2)) {
return (arg1, arg2) => this.runBinary<R, T1, T2>(f, arg1, arg2);
}
void Function() bindCallbackGuarded(void f()) {
return () => this.runGuarded(f);
}
void Function(T) bindUnaryCallbackGuarded<T>(void f(T arg)) {
return (arg) => this.runUnaryGuarded(f, arg);
}
void Function(T1, T2) bindBinaryCallbackGuarded<T1, T2>(
void f(T1 arg1, T2 arg2)) {
return (arg1, arg2) => this.runBinaryGuarded(f, arg1, arg2);
}
operator [](Object key) => null;
// Methods that can be customized by the zone specification.
void handleUncaughtError(error, StackTrace stackTrace) {
_rootHandleUncaughtError(null, null, this, error, stackTrace);
}
Zone fork({ZoneSpecification specification, Map zoneValues}) {
return _rootFork(null, null, this, specification, zoneValues);
}
R run<R>(R f()) {
if (identical(Zone._current, _rootZone)) return f();
return _rootRun(null, null, this, f);
}
R runUnary<R, T>(R f(T arg), T arg) {
if (identical(Zone._current, _rootZone)) return f(arg);
return _rootRunUnary(null, null, this, f, arg);
}
R runBinary<R, T1, T2>(R f(T1 arg1, T2 arg2), T1 arg1, T2 arg2) {
if (identical(Zone._current, _rootZone)) return f(arg1, arg2);
return _rootRunBinary(null, null, this, f, arg1, arg2);
}
ZoneCallback<R> registerCallback<R>(R f()) => f;
ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>(R f(T arg)) => f;
ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>(
R f(T1 arg1, T2 arg2)) =>
f;
AsyncError errorCallback(Object error, StackTrace stackTrace) => null;
void scheduleMicrotask(void f()) {
_rootScheduleMicrotask(null, null, this, f);
}
Timer createTimer(Duration duration, void f()) {
return Timer._createTimer(duration, f);
}
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
return Timer._createPeriodicTimer(duration, f);
}
void print(String line) {
printToConsole(line);
}
}
const _rootZone = const _RootZone();
/**
* Runs [body] in its own zone.
*
* Creates a new zone using [Zone.fork] based on [zoneSpecification] and
* [zoneValues], then runs [body] in that zone and returns the result.
*
* If [onError] is provided, it must have one of the types
* * `void Function(Object)`
* * `void Function(Object, StackTrace)`
* and the [onError] handler is used *both* to handle asynchronous errors
* by overriding [ZoneSpecification.handleUncaughtError] in [zoneSpecification],
* if any, *and* to handle errors thrown synchronously by the call to [body].
*
* If an error occurs synchronously in [body],
* then throwing in the [onError] handler
* makes the call to `runZone` throw that error,
* and otherwise the call to `runZoned` returns `null`.
*
* If the zone specification has a `handleUncaughtError` value or the [onError]
* parameter is provided, the zone becomes an error-zone.
*
* Errors will never cross error-zone boundaries by themselves.
* Errors that try to cross error-zone boundaries are considered uncaught in
* their originating error zone.
*
* var future = new Future.value(499);
* runZoned(() {
* var future2 = future.then((_) { throw "error in first error-zone"; });
* runZoned(() {
* var future3 = future2.catchError((e) { print("Never reached!"); });
* }, onError: (e) { print("unused error handler"); });
* }, onError: (e) { print("catches error of first error-zone."); });
*
* Example:
*
* runZoned(() {
* new Future(() { throw "asynchronous error"; });
* }, onError: print); // Will print "asynchronous error".
*
* It is possible to manually pass an error from one error zone to another
* by re-throwing it in the new zone. If [onError] throws, that error will
* occur in the original zone where [runZoned] was called.
*/
R runZoned<R>(R body(),
{Map zoneValues, ZoneSpecification zoneSpecification, Function onError}) {
if (onError == null) {
return _runZoned<R>(body, zoneValues, zoneSpecification);
}
void Function(Object) unaryOnError;
void Function(Object, StackTrace) binaryOnError;
if (onError is void Function(Object, StackTrace)) {
binaryOnError = onError;
} else if (onError is void Function(Object)) {
unaryOnError = onError;
} else {
throw new ArgumentError("onError callback must take either an Object "
"(the error), or both an Object (the error) and a StackTrace.");
}
HandleUncaughtErrorHandler errorHandler = (Zone self, ZoneDelegate parent,
Zone zone, error, StackTrace stackTrace) {
try {
if (binaryOnError != null) {
self.parent.runBinary(binaryOnError, error, stackTrace);
} else {
assert(unaryOnError != null);
self.parent.runUnary(unaryOnError, error);
}
} catch (e, s) {
if (identical(e, error)) {
parent.handleUncaughtError(zone, error, stackTrace);
} else {
parent.handleUncaughtError(zone, e, s);
}
}
};
if (zoneSpecification == null) {
zoneSpecification =
new ZoneSpecification(handleUncaughtError: errorHandler);
} else {
zoneSpecification = new ZoneSpecification.from(zoneSpecification,
handleUncaughtError: errorHandler);
}
try {
return _runZoned<R>(body, zoneValues, zoneSpecification);
} catch (e, stackTrace) {
if (binaryOnError != null) {
binaryOnError(e, stackTrace);
} else {
assert(unaryOnError != null);
unaryOnError(e);
}
}
return null;
}
/// Runs [body] in a new zone based on [zoneValues] and [specification].
R _runZoned<R>(R body(), Map zoneValues, ZoneSpecification specification) =>
Zone.current
.fork(specification: specification, zoneValues: zoneValues)
.run<R>(body);
You can’t perform that action at this time.