Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(871)

Unified Diff: lib/src/util/cancelable_future.dart

Issue 1262623006: Temporarily bring in code from the async package. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | lib/src/util/forkable_stream.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/util/cancelable_future.dart
diff --git a/lib/src/util/cancelable_future.dart b/lib/src/util/cancelable_future.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e9f858d6989194ff59b0f6abd0dc8f0f97fb496d
--- /dev/null
+++ b/lib/src/util/cancelable_future.dart
@@ -0,0 +1,217 @@
+// Copyright (c) 2015, 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.
+
+// TODO(nweiz): Use this from the async package when
+// https://codereview.chromium.org/1266603005/ lands.
+library test.util.cancelable_future;
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+
+/// A [Future] that can be cancelled.
+///
+/// When this is cancelled, that means it won't complete either successfully or
+/// with an error.
+///
+/// In general it's a good idea to only have a single non-branching chain of
+/// cancellable futures. If there are multiple branches, any of them that aren't
+/// closed explicitly will throw a [CancelException] once one of them is
+/// cancelled.
+class CancelableFuture<T> implements Future<T> {
+ /// The completer that produced this future.
+ ///
+ /// This is canceled when [cancel] is called.
+ final CancelableCompleter<T> _completer;
+
+ /// The future wrapped by [this].
+ Future<T> _inner;
+
+ /// Whether this future has been canceled.
+ ///
+ /// This is tracked individually for each future because a canceled future
+ /// shouldn't emit events, but the completer will throw a [CancelException].
+ bool _canceled = false;
+
+ CancelableFuture._(this._completer, Future<T> inner) {
+ // Once this future is canceled, it should never complete.
+ _inner = inner.whenComplete(() {
+ if (_canceled) return new Completer().future;
+ });
+ }
+
+ /// Creates a [CancelableFuture] wrapping [inner].
+ ///
+ /// When this future is canceled, [onCancel] will be called. The callback may
+ /// return a Future to indicate that asynchronous work has to be done to
+ /// cancel the future; this Future will be returned by [cancel].
+ factory CancelableFuture.fromFuture(Future<T> inner, [onCancel()]) {
+ var completer = new CancelableCompleter<T>(onCancel);
+ completer.complete(inner);
+ return completer.future;
+ }
+
+ /// Creates a [Stream] containing the result of this future.
+ ///
+ /// If this Future is canceled, the Stream will not produce any events. If a
+ /// subscription to the stream is canceled, this is as well.
+ Stream<T> asStream() {
+ var controller = new StreamController<T>(
+ sync: true, onCancel: _completer._cancel);
+
+ _inner.then((value) {
+ controller.add(value);
+ controller.close();
+ }, onError: (error, stackTrace) {
+ controller.addError(error, stackTrace);
+ controller.close();
+ });
+ return controller.stream;
+ }
+
+ /// Returns [this] as a normal future.
+ ///
+ /// The returned future is different from this one in the following ways:
+ ///
+ /// * Its methods don't return [CancelableFuture]s.
+ ///
+ /// * It doesn't support [cancel] or [asFuture].
+ ///
+ /// * The [Stream] returned by [asStream] won't cancel the future if it's
+ /// canceled.
+ ///
+ /// * If a [timeout] times out, it won't cancel the future.
+ Future asFuture() => _inner;
+
+ CancelableFuture catchError(Function onError, {bool test(error)}) =>
kevmoo 2015/07/29 23:03:46 docs for these?
nweiz 2015/07/29 23:18:10 Generally you shouldn't document inherited members
+ new CancelableFuture._(
+ _completer, _inner.catchError(onError, test: test));
+
+ CancelableFuture then(onValue(T value), {Function onError}) =>
+ new CancelableFuture._(
+ _completer, _inner.then(onValue, onError: onError));
+
+ CancelableFuture<T> whenComplete(action()) =>
+ new CancelableFuture<T>._(_completer, _inner.whenComplete(action));
+
+ /// Time-out the future computation after [timeLimit] has passed.
+ ///
+ /// When the future times out, it will be canceled. Note that the return value
+ /// of the completer's `onCancel` callback will be ignored by default, and any
+ /// errors it produces silently dropped. To avoid this, call [cancel]
+ /// explicitly in [onTimeout].
+ CancelableFuture timeout(Duration timeLimit, {onTimeout()}) {
+ var wrappedOnTimeout = () {
+ // Ignore errors here because there's no good way to pipe them to the
+ // caller without screwing up [onTimeout].
+ _completer._cancel().catchError((_) {});
+ if (onTimeout != null) return onTimeout();
+ throw new TimeoutException("Future not completed", timeLimit);
+ };
+
+ return new CancelableFuture._(
+ _completer, _inner.timeout(timeLimit, onTimeout: wrappedOnTimeout));
+ }
+
+ /// Cancels this future.
+ ///
+ /// This returns the [Future] returned by the [CancelableCompleter]'s
+ /// `onCancel` callback. Unlike [Stream.cancel], it never returns `null`.
+ Future cancel() {
+ _canceled = true;
+ return _completer._cancel();
+ }
+}
+
+/// A completer for a [CancelableFuture].
+class CancelableCompleter<T> implements Completer<T> {
+ /// The completer for the wrapped future.
+ final Completer<T> _inner;
+
+ /// The callback to call if the future is canceled.
+ final ZoneCallback _onCancel;
+
+ CancelableFuture<T> get future => _future;
+ CancelableFuture<T> _future;
+
+ bool get isCompleted => _isCompleted;
+ bool _isCompleted = false;
+
+ /// Whether the completer was canceled before being completed.
+ bool get isCanceled => _isCanceled;
+ bool _isCanceled = false;
+
+ /// Whether the completer has fired.
+ ///
+ /// This is distinct from [isCompleted] when a [Future] is passed to
+ /// [complete]; this won't be `true` until that [Future] fires.
+ bool _fired = false;
+
+ /// Creates a new completer for a [CancelableFuture].
+ ///
+ /// When the future is canceled, as long as the completer hasn't yet
+ /// completed, [onCancel] is called. The callback may return a [Future]; if
+ /// so, that [Future] is returned by [CancelableFuture.cancel].
+ CancelableCompleter([this._onCancel])
+ : _inner = new Completer<T>() {
+ _future = new CancelableFuture<T>._(this, _inner.future);
+ }
+
+ void complete([value]) {
+ if (_isCompleted) throw new StateError("Future already completed");
+ _isCompleted = true;
+
+ if (_isCanceled) return;
+ if (value is! Future) {
+ _fired = true;
+ _inner.complete(value);
+ return;
+ }
+
+ value.then((result) {
+ if (_isCanceled) return;
+ _fired = true;
+ _inner.complete(result);
+ }, onError: (error, stackTrace) {
+ if (_isCanceled) return;
+ _fired = true;
+ _inner.completeError(error, stackTrace);
+ });
+ }
+
+ void completeError(Object error, [StackTrace stackTrace]) {
+ if (_isCompleted) throw new StateError("Future already completed");
+ _isCompleted = true;
+
+ if (_isCanceled) return;
+ _fired = true;
+ _inner.completeError(error, stackTrace);
+ }
+
+ /// Cancel the completer.
+ Future _cancel() => _cancelMemo.runOnce(() {
+ if (_fired) return null;
+ _isCanceled = true;
+
+ // Throw an catch to get access to the current stack trace.
+ try {
+ throw new CancelException();
+ } catch (error, stackTrace) {
+ _inner.completeError(error, stackTrace);
+ }
+
+ if (_onCancel != null) return _onCancel();
+ });
+ final _cancelMemo = new AsyncMemoizer();
+}
+
+/// An exception thrown when a [CancelableFuture] is canceled.
+///
+/// Since a canceled [CancelableFuture] doesn't receive any more events, this
+/// will only be passed to other branches of the future chain.
+class CancelException implements Exception {
+ CancelException();
+
+ String toString() => "This Future has been canceled.";
+}
« no previous file with comments | « no previous file | lib/src/util/forkable_stream.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698