blob: bf2f0a998bc990a6214853a4b6b250ed27bdf259 [file] [log] [blame] [edit]
// Copyright (c) 2025, 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.
// Top-level Zone-related declarations.
// Anything public that is not implementing `Zone`, `ZoneDelegate`
// or `ZoneSpecification`.
part of 'dart:async';
/// A no-argument function, like the argument to `Zone.run`.
typedef ZoneCallback<R> = R Function();
/// A one-argument function, like the argument to `Zone.runUnary`.
typedef ZoneUnaryCallback<R, T> = R Function(T);
/// A two-argument function, like the argument to `Zone.runBinary`.
typedef ZoneBinaryCallback<R, T1, T2> = R Function(T1, T2);
/// 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.
///
/// Example use:
/// ```dart
/// var secret = "arglebargle"; // Or a random generated string.
/// var result = runZoned(
/// () async {
/// await Future.delayed(Duration(seconds: 5), () {
/// print("${Zone.current[#_secret]} glop glyf");
/// });
/// },
/// zoneValues: {#_secret: secret},
/// zoneSpecification:
/// ZoneSpecification(print: (Zone self, parent, zone, String value) {
/// if (value.contains(Zone.current[#_secret] as String)) {
/// value = "--censored--";
/// }
/// parent.print(zone, value);
/// }));
/// secret = ""; // Erase the evidence.
/// await result; // Wait for asynchronous computation to complete.
/// ```
/// The new zone intercepts `print` and stores a value under the private
/// symbol `#_secret`. The secret is available from the new [Zone] object,
/// which is the [Zone.current] for the body,
/// and is also the first, `self`, parameter to the `print` handler function.
///
/// If the [ZoneSpecification.handleUncaughtError] is set, or the deprecated
/// [onError] callback is passed, the created zone will be an _error zone_.
/// Asynchronous errors in futures never cross zone boundaries between zones
/// with a different [Zone.errorZone].
/// A consequence of that behavior can be that a [Future] which completes as an
/// error in the created zone will seem to never complete when used from a zone
/// that belongs to a different error zone.
/// Multiple attempts to use the future in a zone where the error is
/// inaccessible will cause the error to be reported *again* in it's original
/// error zone.
///
/// See [runZonedGuarded] in place of using the deprected [onError] argument.
/// If [onError] is provided this function also tries to catch and handle
/// synchronous errors from [body], but may throw an error anyway returning
/// `null` if the generic argument [R] is not nullable.
R runZoned<R>(
R body(), {
Map<Object?, Object?>? zoneValues,
ZoneSpecification? zoneSpecification,
@Deprecated("Use runZonedGuarded instead") Function? onError,
}) {
if (onError != null) {
// TODO: Remove this when code have been migrated off using [onError].
if (onError is! void Function(Object, StackTrace)) {
if (onError is void Function(Object)) {
var originalOnError = onError;
onError = (Object error, StackTrace stack) => originalOnError(error);
} else {
throw ArgumentError.value(
onError,
"onError",
"Must be Function(Object) or Function(Object, StackTrace)",
);
}
}
return runZonedGuarded(
body,
onError,
zoneSpecification: zoneSpecification,
zoneValues: zoneValues,
)
as R;
}
return _runZoned<R>(body, zoneValues, zoneSpecification);
}
/// Runs [body] in its own error zone.
///
/// Creates a new zone using [Zone.fork] based on [zoneSpecification] and
/// [zoneValues], then runs [body] in that zone and returns the result.
///
/// The [onError] function 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 `runZonedGuarded` throw that error,
/// and otherwise the call to `runZonedGuarded` returns `null`.
///
/// The created zone will always be an _error zone_.
/// Asynchronous errors in futures never cross zone boundaries between zones
/// with a different [Zone.errorZone].
/// A consequence of that behavior can be that a [Future] which completes as an
/// error in the created zone will seem to never complete when used from a zone
/// that belongs to a different error zone.
/// Multiple attempts to use the future in a zone where the error is
/// inaccessible will cause the error to be reported *again* in it's original
/// error zone.
R? runZonedGuarded<R>(
R body(),
void onError(Object error, StackTrace stack), {
Map<Object?, Object?>? zoneValues,
ZoneSpecification? zoneSpecification,
}) {
_Zone parentZone = Zone._current;
HandleUncaughtErrorHandler errorHandler =
(
Zone self,
ZoneDelegate parent,
Zone zone,
Object error,
StackTrace stackTrace,
) {
try {
parentZone.runBinary(onError, error, stackTrace);
} catch (e, s) {
if (identical(e, error)) {
parent.handleUncaughtError(zone, error, stackTrace);
} else {
parent.handleUncaughtError(zone, e, s);
}
}
};
if (zoneSpecification == null) {
zoneSpecification = ZoneSpecification(handleUncaughtError: errorHandler);
} else {
zoneSpecification = ZoneSpecification.from(
zoneSpecification,
handleUncaughtError: errorHandler,
);
}
try {
return _runZoned<R>(body, zoneValues, zoneSpecification);
} catch (error, stackTrace) {
onError(error, stackTrace);
}
return null;
}
/// Runs [body] in a new zone based on [zoneValues] and [specification].
R _runZoned<R>(
R body(),
Map<Object?, Object?>? zoneValues,
ZoneSpecification? specification,
) => Zone.current
.fork(specification: specification, zoneValues: zoneValues)
.run<R>(body);