Line data Source code
1 : // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import 'dart:async';
6 :
7 : import 'package:async/async.dart';
8 :
9 : import 'utils.dart';
10 :
11 : /// An asynchronous operation that can be cancelled.
12 : ///
13 : /// The value of this operation is exposed as [value]. When this operation is
14 : /// cancelled, [value] won't complete either successfully or with an error. If
15 : /// [value] has already completed, cancelling the operation does nothing.
16 : class CancelableOperation<T> {
17 : /// The completer that produced this operation.
18 : ///
19 : /// This is canceled when [cancel] is called.
20 : final CancelableCompleter<T> _completer;
21 :
22 0 : CancelableOperation._(this._completer);
23 :
24 : /// Creates a [CancelableOperation] wrapping [inner].
25 : ///
26 : /// When this operation is canceled, [onCancel] will be called and any value
27 : /// or error produced by [inner] will be discarded. If [onCancel] returns a
28 : /// [Future], it will be forwarded to [cancel].
29 : ///
30 : /// [onCancel] will be called synchronously when the operation is canceled.
31 : /// It's guaranteed to only be called once.
32 : factory CancelableOperation.fromFuture(Future<T> inner,
33 : {FutureOr onCancel()}) {
34 0 : var completer = new CancelableCompleter<T>(onCancel: onCancel);
35 0 : completer.complete(inner);
36 0 : return completer.operation;
37 : }
38 :
39 : /// The value returned by the operation.
40 0 : Future<T> get value => _completer._inner.future;
41 :
42 : /// Creates a [Stream] containing the result of this operation.
43 : ///
44 : /// This is like `value.asStream()`, but if a subscription to the stream is
45 : /// canceled, this is as well.
46 : Stream<T> asStream() {
47 : var controller =
48 0 : new StreamController<T>(sync: true, onCancel: _completer._cancel);
49 :
50 0 : value.then((value) {
51 0 : controller.add(value);
52 0 : controller.close();
53 : }, onError: (error, stackTrace) {
54 0 : controller.addError(error, stackTrace);
55 0 : controller.close();
56 : });
57 0 : return controller.stream;
58 : }
59 :
60 : /// Creates a [Future] that completes when this operation completes *or* when
61 : /// it's cancelled.
62 : ///
63 : /// If this operation completes, this completes to the same result as [value].
64 : /// If this operation is cancelled, the returned future waits for the future
65 : /// returned by [cancel], then completes to [cancellationValue].
66 : Future valueOrCancellation([T cancellationValue]) {
67 0 : var completer = new Completer<T>.sync();
68 0 : value.then((result) => completer.complete(result),
69 0 : onError: completer.completeError);
70 :
71 0 : _completer._cancelMemo.future.then((_) {
72 0 : completer.complete(cancellationValue);
73 0 : }, onError: completer.completeError);
74 :
75 0 : return completer.future;
76 : }
77 :
78 : /// Cancels this operation.
79 : ///
80 : /// This returns the [Future] returned by the [CancelableCompleter]'s
81 : /// `onCancel` callback. Unlike [Stream.cancel], it never returns `null`.
82 0 : Future cancel() => _completer._cancel();
83 : }
84 :
85 : /// A completer for a [CancelableOperation].
86 : class CancelableCompleter<T> {
87 : /// The completer for the wrapped future.
88 : final Completer<T> _inner;
89 :
90 : /// The callback to call if the future is canceled.
91 : final FutureOrCallback _onCancel;
92 :
93 : /// Creates a new completer for a [CancelableOperation].
94 : ///
95 : /// When the future operation canceled, as long as the completer hasn't yet
96 : /// completed, [onCancel] is called. If [onCancel] returns a [Future], it's
97 : /// forwarded to [CancelableOperation.cancel].
98 : ///
99 : /// [onCancel] will be called synchronously when the operation is canceled.
100 : /// It's guaranteed to only be called once.
101 : CancelableCompleter({FutureOr onCancel()})
102 : : _onCancel = onCancel,
103 0 : _inner = new Completer<T>() {
104 0 : _operation = new CancelableOperation<T>._(this);
105 : }
106 :
107 : /// The operation controlled by this completer.
108 0 : CancelableOperation<T> get operation => _operation;
109 : CancelableOperation<T> _operation;
110 :
111 : /// Whether the completer has completed.
112 0 : bool get isCompleted => _isCompleted;
113 : bool _isCompleted = false;
114 :
115 : /// Whether the completer was canceled before being completed.
116 0 : bool get isCanceled => _isCanceled;
117 : bool _isCanceled = false;
118 :
119 : /// The memoizer for [_cancel].
120 : final _cancelMemo = new AsyncMemoizer();
121 :
122 : /// Completes [operation] to [value].
123 : ///
124 : /// If [value] is a [Future], this will complete to the result of that
125 : /// [Future] once it completes.
126 : void complete([value]) {
127 0 : if (_isCompleted) throw new StateError("Operation already completed");
128 0 : _isCompleted = true;
129 :
130 0 : if (value is! Future) {
131 0 : if (_isCanceled) return;
132 0 : _inner.complete(value);
133 : return;
134 : }
135 :
136 0 : if (_isCanceled) {
137 : // Make sure errors from [value] aren't top-leveled.
138 0 : value.catchError((_) {});
139 : return;
140 : }
141 :
142 0 : value.then((result) {
143 0 : if (_isCanceled) return;
144 0 : _inner.complete(result);
145 : }, onError: (error, stackTrace) {
146 0 : if (_isCanceled) return;
147 0 : _inner.completeError(error, stackTrace);
148 : });
149 : }
150 :
151 : /// Completes [operation] to [error].
152 : void completeError(Object error, [StackTrace stackTrace]) {
153 0 : if (_isCompleted) throw new StateError("Operation already completed");
154 0 : _isCompleted = true;
155 :
156 0 : if (_isCanceled) return;
157 0 : _inner.completeError(error, stackTrace);
158 : }
159 :
160 : /// Cancel the completer.
161 : Future _cancel() {
162 0 : if (_inner.isCompleted) return new Future.value();
163 :
164 0 : return _cancelMemo.runOnce(() {
165 0 : _isCanceled = true;
166 0 : if (_onCancel != null) return _onCancel();
167 : });
168 : }
169 : }
|