Dart Documentationerror_groupErrorGroup

ErrorGroup class

An ErrorGroup entangles the errors of multiple Futures and Streams with one another. This allows APIs to expose multiple Futures and Streams that have identical error conditions without forcing API consumers to attach error handling to objects they don't care about.

To use an ErrorGroup, register Futures and Streams with it using registerFuture and registerStream. These methods return wrapped versions of the Futures and Streams, which should then be used in place of the originals. For example:

var errorGroup = new ErrorGroup();
future = errorGroup.registerFuture(future);
stream = errorGroup.registerStream(stream);

An ErrorGroup has two major effects on its wrapped members:

  • An error in any member of the group will be propagated to every member that hasn't already completed. If those members later complete, their values will be ignored.

  • If any member of this group has a listener, errors on members without listeners won't get passed to the top-level error handler.

class ErrorGroup {
 /// The [Future]s that are members of [this].
 final _futures = <_ErrorGroupFuture>[];

 /// The [Stream]s that are members of [this].
 final _streams = <_ErrorGroupStream>[];

 /// Whether [this] has completed, either successfully or with an error.
 var _isDone = false;

 /// The [Completer] for [done].
 final _doneCompleter = new Completer();

 /// The underlying [Future] for [done]. We need to be able to access it
 /// internally as an [_ErrorGroupFuture] so we can check if it has listeners
 /// and signal errors on it.
 _ErrorGroupFuture _done;

 /// Returns a [Future] that completes successully when all members of [this]
 /// are complete, or with an error if any member receives an error.
 ///
 /// This [Future] is effectively in the group in that an error on it won't be
 /// passed to the top-level error handler unless no members of the group have
 /// listeners attached.
 Future get done => _done;

 /// Creates a new group with no members.
 ErrorGroup() {
   this._done = new _ErrorGroupFuture(this, _doneCompleter.future);
 }

 /// Registers a [Future] as a member of [this]. Returns a wrapped version of
 /// [future] that should be used in its place.
 ///
 /// If all members of [this] have already completed successfully or with an
 /// error, it's a [StateError] to try to register a new [Future].
 Future registerFuture(Future future) {
   if (_isDone) {
     throw new StateError("Can't register new members on a complete "
         "ErrorGroup.");
   }

   var wrapped = new _ErrorGroupFuture(this, future);
   _futures.add(wrapped);
   return wrapped;
 }

 /// Registers a [Stream] as a member of [this]. Returns a wrapped version of
 /// [stream] that should be used in its place. The returned [Stream] will be
 /// multi-subscription if and only if [stream] is.
 ///
 /// Since all errors in a group are passed to all members, the returned
 /// [Stream] will automatically unsubscribe all its listeners when it
 /// encounters an error.
 ///
 /// If all members of [this] have already completed successfully or with an
 /// error, it's a [StateError] to try to register a new [Stream].
 Stream registerStream(Stream stream) {
   if (_isDone) {
     throw new StateError("Can't register new members on a complete "
         "ErrorGroup.");
   }

   var wrapped = new _ErrorGroupStream(this, stream);
   _streams.add(wrapped);
   return wrapped;
 }

 /// Sends [error] to all members of [this]. Like errors that come from
 /// members, this will only be passed to the top-level error handler if no
 /// members have listeners.
 ///
 /// If all members of [this] have already completed successfully or with an
 /// error, it's a [StateError] to try to signal an error.
 void signalError(var error) {
   if (_isDone) {
     throw new StateError("Can't signal errors on a complete ErrorGroup.");
   }

   _signalError(error);
 }

 /// Signal an error internally. This is just like [signalError], but instead
 /// of throwing an error if [this] is complete, it just does nothing.
 void _signalError(var error) {
   if (_isDone) return;

   var caught = false;
   for (var future in _futures) {
     if (future._isDone || future._hasListeners) caught = true;
     future._signalError(error);
   }

   for (var stream in _streams) {
     if (stream._isDone || stream._hasListeners) caught = true;
     stream._signalError(error);
   }

   _isDone = true;
   _done._signalError(error);
   if (!caught && !_done._hasListeners) error.throwDelayed();
 }

 /// Notifies [this] that one of its member [Future]s is complete.
 void _signalFutureComplete(_ErrorGroupFuture future) {
   if (_isDone) return;

   _isDone = _futures.every((future) => future._isDone) &&
       _streams.every((stream) => stream._isDone);
   if (_isDone) _doneCompleter.complete();
 }

 /// Notifies [this] that one of its member [Stream]s is complete.
 void _signalStreamComplete(_ErrorGroupStream stream) {
   if (_isDone) return;

   _isDone = _futures.every((future) => future._isDone) &&
       _streams.every((stream) => stream._isDone);
   if (_isDone) _doneCompleter.complete();
 }
}

Constructors

new ErrorGroup() #

Creates a new group with no members.

ErrorGroup() {
 this._done = new _ErrorGroupFuture(this, _doneCompleter.future);
}

Properties

final Future done #

Returns a Future that completes successully when all members of this are complete, or with an error if any member receives an error.

This Future is effectively in the group in that an error on it won't be passed to the top-level error handler unless no members of the group have listeners attached.

Future get done => _done;

Methods

Future registerFuture(Future future) #

Registers a Future as a member of this. Returns a wrapped version of future that should be used in its place.

If all members of this have already completed successfully or with an error, it's a StateError to try to register a new Future.

Future registerFuture(Future future) {
 if (_isDone) {
   throw new StateError("Can't register new members on a complete "
       "ErrorGroup.");
 }

 var wrapped = new _ErrorGroupFuture(this, future);
 _futures.add(wrapped);
 return wrapped;
}

Stream registerStream(Stream stream) #

Registers a Stream as a member of this. Returns a wrapped version of stream that should be used in its place. The returned Stream will be multi-subscription if and only if stream is.

Since all errors in a group are passed to all members, the returned Stream will automatically unsubscribe all its listeners when it encounters an error.

If all members of this have already completed successfully or with an error, it's a StateError to try to register a new Stream.

Stream registerStream(Stream stream) {
 if (_isDone) {
   throw new StateError("Can't register new members on a complete "
       "ErrorGroup.");
 }

 var wrapped = new _ErrorGroupStream(this, stream);
 _streams.add(wrapped);
 return wrapped;
}

void signalError(error) #

Sends error to all members of this. Like errors that come from members, this will only be passed to the top-level error handler if no members have listeners.

If all members of this have already completed successfully or with an error, it's a StateError to try to signal an error.

void signalError(var error) {
 if (_isDone) {
   throw new StateError("Can't signal errors on a complete ErrorGroup.");
 }

 _signalError(error);
}