Chromium Code Reviews| Index: sdk/lib/async/zone.dart |
| diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart |
| index 32fb627b39742eb03c7a049ec0fdd20b298eb896..39ba2e0645ccd412af6a46e3027f9f6d7e97ea0b 100644 |
| --- a/sdk/lib/async/zone.dart |
| +++ b/sdk/lib/async/zone.dart |
| @@ -277,14 +277,26 @@ class _ZoneSpecification implements ZoneSpecification { |
| } |
| /** |
| - * This class wraps zones for delegation. |
| + * An adapted view of the parent zone. |
| * |
| - * When forwarding to parent zones one can't just invoke the parent zone's |
| - * exposed functions (like [Zone.run]), but one needs to provide more |
| - * information (like the zone the `run` was initiated). Zone callbacks thus |
| - * receive more information including this [ZoneDelegate] class. When delegating |
| - * to the parent zone one should go through the given instance instead of |
| - * directly invoking 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 { |
| /*=R*/ handleUncaughtError/*<R>*/( |
| @@ -318,117 +330,268 @@ abstract class ZoneDelegate { |
| } |
| /** |
| - * A Zone represents the asynchronous version of a dynamic extent. Asynchronous |
| - * callbacks are executed in the zone they have been queued in. For example, |
| - * the callback of a `future.then` is executed in the same zone as the one where |
| - * the `then` was invoked. |
| + * A zone represents an environment that remains stable across asynchronous |
| + * calls. |
| + * |
| + * Asynchronous callbacks are executed in the zone they have been queued in. For |
| + * example, the callback of a `future.then` is executed in the same zone as the |
| + * one where the `then` was invoked. |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
Consider moving this paragraph somewhere after "Co
floitsch
2016/07/02 03:11:20
Done. Removed.
|
| + * |
| + * 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: |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
Is this repeating paragraph 2 above? (if so, maybe
floitsch
2016/07/02 03:11:19
Done.
|
| + * 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] or [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. |
| */ |
| abstract class Zone { |
| // Private constructor so that it is not possible instantiate a Zone class. |
| Zone._(); |
| - /** The root zone that is implicitly created. */ |
| + /** |
| + * The root zone. |
| + * |
| + * All isolate entry functions (`main` or spawned functions) start running in |
| + * the root zone. Without any custom zone the whole program always runs in the |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
Without any custom zone .. ->
If no custom zone i
floitsch
2016/07/02 03:11:20
Done.
|
| + * root zone and [Zone.current] is always identical to [Zone.ROOT]. |
| + * |
| + * The root zone implements the default behavior of all zone operations. |
| + * Many methods, like [registerCallback] do the bare minimum, others, like |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
... do the bare minimum, and are only provided as
floitsch
2016/07/02 03:11:20
Done.
|
| + * [scheduleMicrotask] interact with the underlying system to implement the |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
comma before interact.
floitsch
2016/07/02 03:11:19
Done.
|
| + * desired behavior. |
| + */ |
| static const Zone ROOT = _ROOT_ZONE; |
| /** The currently running zone. */ |
| static Zone _current = _ROOT_ZONE; |
| + /** 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, |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
Start with capital letter.
I'd prefer a comma inst
floitsch
2016/07/02 03:11:19
Done.
|
| + * a `throw` in [Timer.run]. |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:02
... a throw in the function passed to [Timer.run].
floitsch
2016/07/02 03:11:19
Done.
|
| + * 2. asynchronous errors that are pushed down [Future] and [Stream] chains |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
Start with capital letter again.
maybe: down -> th
floitsch
2016/07/02 03:11:20
Done.
|
| + * but for which no child registered an error handled. |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
Comma before but (it's optional, but I would prefe
floitsch
2016/07/02 03:11:19
Done.
|
| + * 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 synchronous uncaught exceptions. |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
I'd say "uncaught synchronous exceptions".
I just
floitsch
2016/07/02 03:11:19
Done.
|
| + */ |
| /*=R*/ handleUncaughtError/*<R>*/(error, StackTrace stackTrace); |
| /** |
| * Returns the parent zone. |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
Describe a getter like a property, not a method. I
floitsch
2016/07/02 03:11:20
Done.
|
| * |
| * Returns `null` if `this` is the [ROOT] zone. |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
-> Is `null` if ...
floitsch
2016/07/02 03:11:20
Done.
|
| + * |
| + * 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. |
| - * Errors are not allowed to cross between zones with different error-zones. |
| * |
| - * This is the closest parent or ancestor zone of this zone that has a custom |
| + * 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 can not enter a child zone with a different error handler |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
can not -> cannot (or can't).
floitsch
2016/07/02 03:11:20
Done.
|
| + * 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 inherit their |
| - * [handleUncaughtError] callback from the same [errorZone]. |
| + * 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 will have behavior like the current zone, except where |
| - * overridden by functions in [specification]. |
| + * 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 will have the same stored values (accessed through |
| - * `operator []`) as this zone, but updated with the keys and values |
| - * in [zoneValues]. If a key is in both this zone's values and in |
| - * `zoneValues`, the new zone will use the value from `zoneValues``. |
| + * The new zone inherits the stored values (accessed through `operator []`) |
|
Lasse Reichstein Nielsen
2016/07/04 14:14:19
[operator []]
(if you can write that in DartDoc
floitsch
2016/07/05 15:39:57
Dartdoc doesn't link it, but at least doesn't "bre
|
| + * 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 }); |
| + Zone fork({ZoneSpecification specification, |
| + Map zoneValues}); |
| /** |
| - * Executes the given function [f] in this zone. |
| + * 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*/ f()); |
| + /*=R*/ run/*<R>*/(/*=R*/ action()); |
| /** |
| - * Executes the given callback [f] with argument [arg] in this zone. |
| + * 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*/ f(/*=T*/ arg), /*=T*/ arg); |
| + /*=R*/ runUnary/*<R, T>*/(/*=R*/ action(/*=T*/ argument), /*=T*/ argument); |
| /** |
| - * Executes the given callback [f] with argument [arg1] and [arg2] in this |
| + * 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*/ f(/*=T1*/ arg1, /*=T2*/ arg2), /*=T1*/ arg1, /*=T2*/ arg2); |
| + /*=R*/ action(/*=T1*/ argument1, /*=T2*/ argument2), /*=T1*/ argument1, |
| + /*=T2*/ argument2); |
| /** |
| - * Executes the given function [f] in this zone. |
| + * Executes the given [action] in this zone and catches synchronous |
| + * errors. |
| + * |
| + * This function is equivalent to: |
| + * ``` |
| + * try { |
| + * return this.run(action); |
| + * } catch (e, s) { |
| + * return this.handleUncaughtError(e, s); |
| + * } |
| + * ``` |
| * |
| - * Same as [run] but catches uncaught errors and gives them to |
| - * [handleUncaughtError]. |
| + * See [run]. |
| */ |
| - /*=R*/ runGuarded/*<R>*/(/*=R*/ f()); |
| + /*=R*/ runGuarded/*<R>*/(/*=R*/ action()); |
| /** |
| - * Executes the given callback [f] in this zone. |
| + * Executes the given [action] with [argument] in this zone and |
| + * catches synchronous errors. |
| * |
| - * Same as [runUnary] but catches uncaught errors and gives them to |
| - * [handleUncaughtError]. |
| + * See [runGuarded]. |
| */ |
| - /*=R*/ runUnaryGuarded/*<R, T>*/(/*=R*/ f(/*=T*/ arg), /*=T*/ arg); |
| + /*=R*/ runUnaryGuarded/*<R, T>*/(/*=R*/ action(/*=T*/ argument), |
| + /*=T*/ argument); |
| /** |
| - * Executes the given callback [f] in this zone. |
| + * Executes the given [action] with [argument1] and [argument2] in this |
| + * zone and catches synchronous errors. |
| * |
| - * Same as [runBinary] but catches uncaught errors and gives them to |
| - * [handleUncaughtError]. |
| + * See [runGuarded]. |
| */ |
| /*=R*/ runBinaryGuarded/*<R, T1, T2>*/( |
| - /*=R*/ f(/*=T1*/ arg1, /*=T2*/ arg2), /*=T1*/ arg1, /*=T2*/ arg2); |
| + /*=R*/ action(/*=T1*/ argument1, /*=T2*/ argument2), /*=T1*/ argument1, |
| + /*=T2*/ argument2); |
| /** |
| * Registers the given callback in this zone. |
| * |
| - * It is good practice to register asynchronous or delayed callbacks before |
| - * invoking [run]. This gives the zone a chance to wrap the callback and |
| - * to store information with the callback. For example, a zone may decide |
| + * 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 a potentially new callback that should be used in place of the |
| - * given [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()); |
| @@ -451,86 +614,146 @@ abstract class Zone { |
| /** |
| * Equivalent to: |
| * |
| - * ZoneCallback registered = registerCallback(f); |
| + * ZoneCallback registered = this.registerCallback(action); |
| * if (runGuarded) return () => this.runGuarded(registered); |
| * return () => this.run(registered); |
| * |
| */ |
| ZoneCallback/*<R>*/ bindCallback/*<R>*/( |
| - /*=R*/ f(), { bool runGuarded: true }); |
| + /*=R*/ action(), { bool runGuarded: true }); |
| /** |
| * Equivalent to: |
| * |
| - * ZoneCallback registered = registerUnaryCallback(f); |
| + * ZoneCallback registered = this.registerUnaryCallback(action); |
| * if (runGuarded) return (arg) => this.runUnaryGuarded(registered, arg); |
| * return (arg) => thin.runUnary(registered, arg); |
| */ |
| ZoneUnaryCallback/*<R, T>*/ bindUnaryCallback/*<R, T>*/( |
| - /*=R*/ f(/*=T*/ arg), { bool runGuarded: true }); |
| + /*=R*/ action(/*=T*/ argument), { bool runGuarded: true }); |
| /** |
| * Equivalent to: |
| * |
| - * ZoneCallback registered = registerBinaryCallback(f); |
| + * ZoneCallback registered = registerBinaryCallback(action); |
| * if (runGuarded) { |
| * return (arg1, arg2) => this.runBinaryGuarded(registered, arg); |
| * } |
| * return (arg1, arg2) => thin.runBinary(registered, arg1, arg2); |
| */ |
| ZoneBinaryCallback/*<R, T1, T2>*/ bindBinaryCallback/*<R, T1, T2>*/( |
| - /*=R*/ f(/*=T1*/ arg1, /*=T2*/ arg2), { bool runGuarded: true }); |
| + /*=R*/ action(/*=T1*/ argument1, /*=T2*/ argument2), |
| + { bool runGuarded: true }); |
| /** |
| - * Intercepts errors when added programmatically to a `Future` or `Stream`. |
| + * Intercepts errors when added programatically to a `Future` or `Stream`. |
| + * |
| + * When calling [Completer.completeError], [Stream.addError], |
| + * or for some [Future] constructors the current zone is allowed to intercept |
|
Lasse Reichstein Nielsen
2016/07/01 11:25:01
remove "for". Consider a comma before "the current
floitsch
2016/07/02 03:11:19
Done.
|
| + * 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]. |
| * |
| - * When caling [Completer.completeError], [Stream.addError], |
| - * or [Future] constructors that take an error or a callback that may throw, |
| - * the current zone is allowed to intercept and replace the error. |
| + * 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. |
| * |
| - * When other libraries use intermediate controllers or completers, such |
| - * calls may contain errors that have already been processed. |
| + * Returns `null` if no replacement is desired. Otherwise returns an instance |
| + * of [AsyncError] holding the new pair of error and stack trace. |
| * |
| - * Return `null` if no replacement is desired. |
| - * The original error is used unchanged in that case. |
| - * Otherwise return an instance of [AsyncError] holding |
| - * the new pair of error and stack trace. |
| - * If the [AsyncError.error] is `null`, it is replaced by a [NullThrownError]. |
| + * 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 [f] asynchronously in this zone. |
| + * Runs [action] 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 [action]). |
| */ |
| - void scheduleMicrotask(void f()); |
| + void scheduleMicrotask(void action()); |
| /** |
| - * Creates a task, given a [create] function and a [specification]. |
| + * Creates a task in the current zone. |
| * |
| - * The [create] function is invoked with the [specification] as argument. It |
| - * returns a task object which is used for all future interactions with the |
| - * zone. |
| + * A task represents an asynchronous operation or process that reports back |
| + * through the event loop. |
| * |
| - * Custom zones may replace the [specification] with a different one, thus |
| - * modifying the task parameters. |
| + * This function allows the zone to intercept the initialization of the |
| + * task while the [runTask] function is invoked when the task reports back. |
| + * |
| + * By default, in the root zone, the [create] function is invoked with the |
| + * [specification] as argument. It returns a task object which is used for all |
| + * future interactions between the zone and the task. The object is |
| + * a unique instance representing the task. It is generally returned to |
| + * whoever initiated the task. |
| + * For example, the HTML library uses the returned [StreamSubscription] as |
| + * task object when users register an event listener. |
| * |
| - * Tasks are created when the program is starting an operation that returns |
| - * through the event loop. For example, a timer or an http request both |
| + * Tasks are created when the program starts an operation that reports back |
| + * through the event loop. For example, a timer or an HTTP request both |
| * return through the event loop and are therefore tasks. |
| * |
| * If the [create] function is not invoked (because a custom zone has |
| * replaced or intercepted it), then the operation is *not* started. This |
| - * means that a custom zone can intercept tasks, like http requests. |
| + * means that a custom zone can intercept tasks, like HTTP requests. |
| + * |
| + * A task goes through the following steps: |
| + * - a user invokes a library function that should eventually return through |
| + * the event loop. |
| + * - the library function creates a [TaskSpecification] that contains the |
| + * necessary information to start the operation, and invokes |
| + * `Zone.current.createTask` with the specification and a [create] closure. |
| + * The closure, when invoked, uses the specification to start the operation |
| + * (usually by interacting with the underlying system, or as a native |
| + * extension), and returns a task object that identifies the running task. |
| + * - custom zones handle the request and (unless completely intercepted and |
| + * aborted), end up calling the root zone's [createTask] which runs the |
| + * provided `create` closure, which may have been replaced at this point. |
| + * - later, the asynchronous operation returns through the event loop. |
| + * It invokes [Zone.runTask] on the zone in which the task should run |
| + * (and which was originally passed to the `create` function by |
| + * `createTask`). The [runTask] function receives the |
| + * task object, a `run` function and an argument. As before, custom zones |
| + * may intercept this call. Eventually (unless aborted), the `run` function |
| + * is invoked. This last step may happen multiple times for tasks that are |
| + * not oneshot tasks (see [ZoneSpecification.isOneShot]). |
| + * |
| + * Custom zones may replace the [specification] with a different one, thus |
| + * modifying the task parameters. An operation that wishes to be an |
| + * interceptable task must publicly specify the types that intercepting code |
| + * sees: |
| + * - The specification type (extending [TaskSpecification]) which holds the |
| + * information available when intercepting the `createTask` call. |
| + * - The task object type, returned by `createTask` and [create]. This object |
| + * may simply be typed as [Object]. |
| + * - The argument type, if [runTask] takes a meaningful argument. |
| * |
| * *Experimental*. Might disappear without notice. |
| */ |
| - Object/*=T*/ createTask/*<T, S>*/( |
| - TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification); |
| + Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/( |
| + /*=T*/ create(S specification, Zone zone), |
| + TaskSpecification/*=S*/ specification); |
| /** |
| * Runs a task callback. |
| * |
| - * This function is invoked, when an operation, started through [createTask], |
| + * This function is invoked when an operation, started through [createTask], |
| * returns to Dart code. |
| * |
| * Generally, tasks schedule Dart code in the global event loop. As such, |
| @@ -538,14 +761,17 @@ abstract class Zone { |
| * |
| * The [task] object must be the same as the one created with [createTask]. |
| * |
| - * It is good practice, if task operations provide a meaningful [arg], so |
| + * It is good practice, if task operations provide a meaningful [argument], so |
| * that custom zones can deal with it. They might want to log it, or |
| * replace it. |
| * |
| + * See [createTask]. |
| + * |
| * *Experimental*. Might disappear without notice. |
| */ |
| void runTask/*<T, A>*/( |
| - TaskRun/*<T, A>*/ run, Object/*=T*/ task, Object/*=A*/ arg); |
| + /*=T*/ run(/*=T*/ task, /*=A*/ argument), Object/*=T*/ task, |
| + Object/*=A*/ argument); |
| /** |
| * Creates a Timer where the callback is executed in this zone. |
| @@ -561,6 +787,24 @@ abstract class Zone { |
| /** |
| * 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); |