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 : /// 33 : /// Calling this constructor is equivalent to creating a [CancelableCompleter] 34 : /// and completing it with [inner]. As such, [isCompleted] is true from the 35 : /// moment this [CancelableOperation] is created, regardless of whether 36 : /// [inner] has completed yet or not. 37 0 : factory CancelableOperation.fromFuture(Future<T> inner, 38 : {FutureOr Function()? onCancel}) { 39 0 : var completer = CancelableCompleter<T>(onCancel: onCancel); 40 0 : completer.complete(inner); 41 0 : return completer.operation; 42 : } 43 : 44 : /// The value returned by the operation. 45 0 : Future<T> get value => _completer._inner.future; 46 : 47 : /// Creates a [Stream] containing the result of this operation. 48 : /// 49 : /// This is like `value.asStream()`, but if a subscription to the stream is 50 : /// canceled, this is as well. 51 0 : Stream<T> asStream() { 52 : var controller = 53 0 : StreamController<T>(sync: true, onCancel: _completer._cancel); 54 : 55 0 : value.then((value) { 56 0 : controller.add(value); 57 0 : controller.close(); 58 0 : }, onError: (Object error, StackTrace stackTrace) { 59 0 : controller.addError(error, stackTrace); 60 0 : controller.close(); 61 : }); 62 0 : return controller.stream; 63 : } 64 : 65 : /// Creates a [Future] that completes when this operation completes *or* when 66 : /// it's cancelled. 67 : /// 68 : /// If this operation completes, this completes to the same result as [value]. 69 : /// If this operation is cancelled, the returned future waits for the future 70 : /// returned by [cancel], then completes to [cancellationValue]. 71 0 : Future<T?> valueOrCancellation([T? cancellationValue]) { 72 0 : var completer = Completer<T?>.sync(); 73 0 : value.then((result) => completer.complete(result), 74 0 : onError: completer.completeError); 75 : 76 0 : _completer._cancelMemo.future.then((_) { 77 0 : completer.complete(cancellationValue); 78 0 : }, onError: completer.completeError); 79 : 80 0 : return completer.future; 81 : } 82 : 83 : /// Registers callbacks to be called when this operation completes. 84 : /// 85 : /// [onValue] and [onError] behave in the same way as [Future.then]. 86 : /// 87 : /// If [onCancel] is provided, and this operation is canceled, the [onCancel] 88 : /// callback is called and the returned operation completes with the result. 89 : /// 90 : /// If [onCancel] is not given, and this operation is canceled, then the 91 : /// returned operation is canceled. 92 : /// 93 : /// If [propagateCancel] is `true` and the returned operation is canceled then 94 : /// this operation is canceled. The default is `false`. 95 0 : CancelableOperation<R> then<R>(FutureOr<R> Function(T) onValue, 96 : {FutureOr<R> Function(Object, StackTrace)? onError, 97 : FutureOr<R> Function()? onCancel, 98 : bool propagateCancel = false}) { 99 : final completer = 100 0 : CancelableCompleter<R>(onCancel: propagateCancel ? cancel : null); 101 : 102 0 : valueOrCancellation().then((T? result) { 103 0 : if (!completer.isCanceled) { 104 0 : if (isCompleted) { 105 0 : assert(result is T); 106 0 : completer.complete(Future.sync(() => onValue(result as T))); 107 : } else if (onCancel != null) { 108 0 : completer.complete(Future.sync(onCancel)); 109 : } else { 110 0 : completer._cancel(); 111 : } 112 : } 113 0 : }, onError: (Object error, StackTrace stackTrace) { 114 0 : if (!completer.isCanceled) { 115 : if (onError != null) { 116 0 : completer.complete(Future.sync(() => onError(error, stackTrace))); 117 : } else { 118 0 : completer.completeError(error, stackTrace); 119 : } 120 : } 121 : }); 122 0 : return completer.operation; 123 : } 124 : 125 : /// Cancels this operation. 126 : /// 127 : /// This returns the [Future] returned by the [CancelableCompleter]'s 128 : /// `onCancel` callback. Unlike [Stream.cancel], it never returns `null`. 129 0 : Future cancel() => _completer._cancel(); 130 : 131 : /// Whether this operation has been canceled before it completed. 132 0 : bool get isCanceled => _completer.isCanceled; 133 : 134 : /// Whether the [CancelableCompleter] backing this operation has been 135 : /// completed. 136 : /// 137 : /// This value being true does not imply that the [value] future has 138 : /// completed, but merely that it is no longer possible to [cancel] the 139 : /// operation. 140 0 : bool get isCompleted => _completer.isCompleted; 141 : } 142 : 143 : /// A completer for a [CancelableOperation]. 144 : class CancelableCompleter<T> { 145 : /// The completer for the wrapped future. 146 : final _inner = Completer<T>(); 147 : 148 : /// The callback to call if the future is canceled. 149 : final FutureOrCallback? _onCancel; 150 : 151 : /// Creates a new completer for a [CancelableOperation]. 152 : /// 153 : /// When the future operation canceled, as long as the completer hasn't yet 154 : /// completed, [onCancel] is called. If [onCancel] returns a [Future], it's 155 : /// forwarded to [CancelableOperation.cancel]. 156 : /// 157 : /// [onCancel] will be called synchronously when the operation is canceled. 158 : /// It's guaranteed to only be called once. 159 0 : CancelableCompleter({FutureOr Function()? onCancel}) : _onCancel = onCancel; 160 : 161 : /// The operation controlled by this completer. 162 : late final operation = CancelableOperation<T>._(this); 163 : 164 : /// Whether the completer has completed. 165 0 : bool get isCompleted => _isCompleted; 166 : bool _isCompleted = false; 167 : 168 : /// Whether the completer was canceled before being completed. 169 0 : bool get isCanceled => _isCanceled; 170 : bool _isCanceled = false; 171 : 172 : /// The memoizer for [_cancel]. 173 : final _cancelMemo = AsyncMemoizer(); 174 : 175 : /// Completes [operation] to [value]. 176 : /// 177 : /// If [value] is a [Future], this will complete to the result of that 178 : /// [Future] once it completes. 179 0 : void complete([FutureOr<T>? value]) { 180 0 : if (_isCompleted) throw StateError('Operation already completed'); 181 0 : _isCompleted = true; 182 : 183 0 : if (value is! Future) { 184 0 : if (_isCanceled) return; 185 0 : _inner.complete(value); 186 : return; 187 : } 188 : 189 : final future = value as Future<T>; 190 0 : if (_isCanceled) { 191 : // Make sure errors from [value] aren't top-leveled. 192 0 : future.catchError((_) {}); 193 : return; 194 : } 195 : 196 0 : future.then((result) { 197 0 : if (_isCanceled) return; 198 0 : _inner.complete(result); 199 0 : }, onError: (Object error, StackTrace stackTrace) { 200 0 : if (_isCanceled) return; 201 0 : _inner.completeError(error, stackTrace); 202 : }); 203 : } 204 : 205 : /// Completes [operation] to [error]. 206 0 : void completeError(Object error, [StackTrace? stackTrace]) { 207 0 : if (_isCompleted) throw StateError('Operation already completed'); 208 0 : _isCompleted = true; 209 : 210 0 : if (_isCanceled) return; 211 0 : _inner.completeError(error, stackTrace); 212 : } 213 : 214 : /// Cancel the completer. 215 0 : Future _cancel() { 216 0 : if (_inner.isCompleted) return Future.value(); 217 : 218 0 : return _cancelMemo.runOnce(() { 219 0 : _isCanceled = true; 220 0 : var onCancel = _onCancel; 221 : if (onCancel != null) return onCancel(); 222 : }); 223 : } 224 : }