| // Copyright (c) 2014, 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. |
| |
| import 'dart:async'; |
| |
| /** |
| * Type of callback called when the future returned by a CancelableCompleter |
| * is canceled. |
| */ |
| typedef void CancelHandler(); |
| |
| /** |
| * A way to produce [CancelableFuture] objects and to complete them later with |
| * a value or error. |
| * |
| * This class behaves like the standard library [Completer] class, except that |
| * its [future] getter returns a [CancelableFuture]. |
| * |
| * If the future is canceled before being completed, the [CancelHandler] which |
| * was passed to the constructor is invoked, and any further attempt to |
| * complete the future has no effect. For example, in the following code: |
| * |
| * main() { |
| * var cc = new CancelableCompleter(() { |
| * print('cancelled'); // (2) |
| * }); |
| * cc.future.then((value) { |
| * print('completed with value $value'); |
| * }, onError: (error) { |
| * print('completed with error $error'); // (3) |
| * }); |
| * cc.future.cancel(); // (1) |
| * } |
| * |
| * The call at (1) causes (2) to be invoked immediately. (3) will be invoked |
| * later (on a microtask), with an error that is an instance of |
| * [FutureCanceledError]. |
| * |
| * Note that since the closure passed to then() is executed on a microtask, |
| * there is a short window of time between the call to [complete] and the |
| * client being informed that the future has completed. During this window, |
| * any attempt to cancel the future will have no effect. For example, in the |
| * following code: |
| * |
| * main() { |
| * var cc = new CancelableCompleter(() { |
| * print('cancelled'); // (3) |
| * }); |
| * cc.future.then((value) { |
| * print('completed with value $value'); // (4) |
| * }, onError: (error) { |
| * print('completed with error $error'); |
| * }); |
| * cc.complete(100); // (1) |
| * cc.future.cancel(); // (2) |
| * } |
| * |
| * The call at (1) will place the completer in the "completed" state, so the |
| * call at (2) will have no effect (in particular, (3) won't ever execute). |
| * Later, (4) will be invoked on a microtask. |
| */ |
| class CancelableCompleter<T> implements Completer<T> { |
| /** |
| * The completer which holds the state of the computation. If the |
| * computation is canceled, this completer will remain in the non-completed |
| * state. |
| */ |
| final Completer<T> _innerCompleter = new Completer<T>.sync(); |
| |
| /** |
| * The completer which holds the future that is exposed to the client |
| * through [future]. If the computation is canceled, this completer will |
| * be completed with a FutureCanceledError. |
| */ |
| final Completer<T> _outerCompleter = new Completer<T>(); |
| |
| /** |
| * The callback to invoke if the 'cancel' method is called on the future |
| * returned by [future]. This callback will only be invoked if the future |
| * is canceled before being completed. |
| */ |
| final CancelHandler _onCancel; |
| |
| _CancelableCompleterFuture<T> _future; |
| |
| /** |
| * Create a CancelableCompleter that will invoke the given callback |
| * synchronously if its future is canceled. The callback will not be |
| * invoked if the future is completed before being canceled. |
| */ |
| CancelableCompleter(this._onCancel) { |
| _future = new _CancelableCompleterFuture<T>(this); |
| |
| // When the client completes the inner completer, we need to check whether |
| // the outer completer has been completed. If it has, then the operation |
| // was canceled before it finished, and it's too late to un-cancel it, so |
| // we just ignore the result from the inner completer. If it hasn't, then |
| // we simply pass along the result from the inner completer to the outer |
| // completer. |
| // |
| // Note that the reason it is safe for the inner completer to be |
| // synchronous is that we don't expose its future to client code, and we |
| // only use it to complete the outer completer (which is asynchronous). |
| _innerCompleter.future.then((T value) { |
| if (!_outerCompleter.isCompleted) { |
| _outerCompleter.complete(value); |
| } |
| }, onError: (Object error, StackTrace stackTrace) { |
| if (!_outerCompleter.isCompleted) { |
| _outerCompleter.completeError(error, stackTrace); |
| } |
| }); |
| } |
| |
| /** |
| * The [CancelableFuture] that will contain the result provided to this |
| * completer. |
| */ |
| @override |
| CancelableFuture<T> get future => _future; |
| |
| /** |
| * Whether the future has been completed. This is independent of whether |
| * the future has been canceled. |
| */ |
| @override |
| bool get isCompleted => _innerCompleter.isCompleted; |
| |
| /** |
| * Complete [future] with the supplied value. If the future has previously |
| * been canceled, this will have no effect on [future], however it will |
| * still set [isCompleted] to true. |
| */ |
| @override |
| void complete([value]) { |
| _innerCompleter.complete(value); |
| } |
| |
| /** |
| * Complete [future] with an error. If the future has previously been |
| * canceled, this will have no effect on [future], however it will still set |
| * [isCompleted] to true. |
| */ |
| @override |
| void completeError(Object error, [StackTrace stackTrace]) { |
| _innerCompleter.completeError(error, stackTrace); |
| } |
| |
| void _cancel() { |
| if (!_outerCompleter.isCompleted) { |
| _outerCompleter.completeError(new FutureCanceledError()); |
| _onCancel(); |
| } |
| } |
| } |
| |
| /** |
| * An object representing a delayed computation that can be canceled. |
| */ |
| abstract class CancelableFuture<T> implements Future<T> { |
| /** |
| * A CancelableFuture containing the result of calling [computation] |
| * asynchronously. Since the computation is started without delay, calling |
| * the future's cancel method will have no effect. |
| */ |
| factory CancelableFuture(computation()) => |
| new _WrappedFuture<T>(new Future<T>(computation)); |
| |
| /** |
| * A CancelableFuture containing the result of calling [computation] after |
| * [duration] has passed. |
| * |
| * TODO(paulberry): if the future is canceled before the duration has |
| * elapsed, the computation should not be performed. |
| */ |
| factory CancelableFuture.delayed(Duration duration, [computation()]) => |
| new _WrappedFuture<T>(new Future<T>.delayed(duration, computation)); |
| |
| /** |
| * A CancelableFuture that completes with error. Since the future is |
| * completed without delay, calling the future's cancel method will have no |
| * effect. |
| */ |
| factory CancelableFuture.error(Object error, [StackTrace stackTrace]) => |
| new _WrappedFuture<T>(new Future<T>.error(error, stackTrace)); |
| |
| /** |
| * A CancelableFuture containing the result of calling [computation] |
| * asynchronously with scheduleMicrotask. Since the computation is started |
| * without delay, calling the future's cancel method will have no effect. |
| */ |
| factory CancelableFuture.microtask(computation()) => |
| new _WrappedFuture<T>(new Future<T>.microtask(computation)); |
| |
| /** |
| * A CancelableFuture containing the result of immediately calling |
| * [computation]. Since the computation is started without delay, calling |
| * the future's cancel method will have no effect. |
| */ |
| factory CancelableFuture.sync(computation()) => |
| new _WrappedFuture<T>(new Future<T>.sync(computation)); |
| |
| /** |
| * A CancelableFuture whose value is available in the next event-loop |
| * iteration. Since the value is available without delay, calling the |
| * future's cancel method will have no effect. |
| */ |
| factory CancelableFuture.value([value]) => |
| new _WrappedFuture<T>(new Future<T>.value(value)); |
| |
| /** |
| * If the delayed computation has not yet completed, attempt to cancel it. |
| * Note that the cancellation is not always possible. If the computation |
| * could be canceled, the future is completed with a FutureCanceledError. |
| * Otherwise it will behave as though cancel() was not called. |
| * |
| * Note that attempting to cancel a future that has already completed will |
| * never succeed--futures that have already completed retain their final |
| * state forever. |
| */ |
| void cancel(); |
| } |
| |
| /** |
| * Error which is used to complete any [CancelableFuture] which has been |
| * successfully canceled by calling its 'cancel' method. |
| */ |
| class FutureCanceledError {} |
| |
| class _CancelableCompleterFuture<T> implements CancelableFuture<T> { |
| final CancelableCompleter<T> _completer; |
| |
| _CancelableCompleterFuture(this._completer); |
| |
| @override |
| Stream<T> asStream() { |
| // TODO(paulberry): Implement this in such a way that |
| // StreamSubscription.cancel() cancels the future. |
| return _completer._outerCompleter.future.asStream(); |
| } |
| |
| @override |
| void cancel() { |
| _completer._cancel(); |
| } |
| |
| @override |
| Future<T> catchError(Function onError, {bool test(Object error)}) => |
| _completer._outerCompleter.future.catchError(onError, test: test); |
| |
| @override |
| Future<S> then<S>(FutureOr<S> onValue(T value), {Function onError}) => |
| _completer._outerCompleter.future.then(onValue, onError: onError); |
| |
| @override |
| Future<T> timeout(Duration timeLimit, {onTimeout()}) { |
| // TODO(paulberry): Implement this in such a way that a timeout cancels |
| // the future. |
| return _completer._outerCompleter.future |
| .timeout(timeLimit, onTimeout: onTimeout); |
| } |
| |
| @override |
| Future<T> whenComplete(action()) => |
| _completer._outerCompleter.future.whenComplete(action); |
| } |
| |
| /** |
| * A CancelableFuture that wraps an ordinary Future. Attempting to cancel a |
| * _WrappedFuture has no effect. |
| */ |
| class _WrappedFuture<T> implements CancelableFuture<T> { |
| final Future<T> _future; |
| |
| _WrappedFuture(this._future); |
| |
| @override |
| Stream<T> asStream() => _future.asStream(); |
| |
| @override |
| void cancel() {} |
| |
| @override |
| Future<T> catchError(Function onError, {bool test(Object error)}) => |
| _future.catchError(onError, test: test); |
| |
| @override |
| Future<S> then<S>(FutureOr<S> onValue(T value), {Function onError}) => |
| _future.then(onValue, onError: onError); |
| |
| @override |
| Future<T> timeout(Duration timeLimit, {onTimeout()}) => |
| _future.timeout(timeLimit, onTimeout: onTimeout); |
| |
| @override |
| Future<T> whenComplete(action()) => _future.whenComplete(action); |
| } |