OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 library cancelable_future; | |
6 | |
7 import 'dart:async'; | |
8 | |
9 /** | |
10 * Type of callback called when the future returned by a CancelableCompleter | |
11 * is canceled. | |
12 */ | |
13 typedef void CancelHandler(); | |
14 | |
15 /** | |
16 * A way to produce [CancelableFuture] objects and to complete them later with | |
17 * a value or error. | |
18 * | |
19 * This class behaves like the standard library [Completer] class, except that | |
20 * its [future] getter returns a [CancelableFuture]. | |
21 * | |
22 * If the future is canceled before being completed, the [CancelHandler] which | |
23 * was passed to the constructor is invoked, and any further attempt to | |
24 * complete the future has no effect. For example, in the following code: | |
25 * | |
26 * main() { | |
27 * var cc = new CancelableCompleter(() { | |
28 * print('cancelled'); // (2) | |
29 * }); | |
30 * cc.future.then((value) { | |
31 * print('completed with value $value'); | |
32 * }, onError: (error) { | |
33 * print('completed with error $error'); // (3) | |
34 * }); | |
35 * cc.future.cancel(); // (1) | |
36 * } | |
37 * | |
38 * The call at (1) causes (2) to be invoked immediately. (3) will be invoked | |
39 * later (on a microtask), with an error that is an instance of | |
40 * [FutureCanceledError]. | |
41 * | |
42 * Note that since the closure passed to then() is executed on a microtask, | |
43 * there is a short window of time between the call to [complete] and the | |
44 * client being informed that the future has completed. During this window, | |
45 * any attempt to cancel the future will have no effect. For example, in the | |
46 * following code: | |
47 * | |
48 * main() { | |
49 * var cc = new CancelableCompleter(() { | |
50 * print('cancelled'); // (3) | |
51 * }); | |
52 * cc.future.then((value) { | |
53 * print('completed with value $value'); // (4) | |
54 * }, onError: (error) { | |
55 * print('completed with error $error'); | |
56 * }); | |
57 * cc.complete(100); // (1) | |
58 * cc.future.cancel(); // (2) | |
59 * } | |
60 * | |
61 * The call at (1) will place the completer in the "completed" state, so the | |
62 * call at (2) will have no effect (in particular, (3) won't ever execute). | |
63 * Later, (4) will be invoked on a microtask. | |
64 */ | |
65 class CancelableCompleter<T> implements Completer<T> { | |
66 /** | |
67 * The completer which holds the state of the computation. If the | |
68 * computation is canceled, this completer will remain in the non-completed | |
69 * state. | |
70 */ | |
71 final Completer<T> _innerCompleter = new Completer<T>.sync(); | |
72 | |
73 /** | |
74 * The completer which holds the future that is exposed to the client | |
75 * through [future]. If the computation is canceled, this completer will | |
76 * be completed with a FutureCanceledError. | |
77 */ | |
78 final Completer<T> _outerCompleter = new Completer<T>(); | |
79 | |
80 /** | |
81 * The callback to invoke if the 'cancel' method is called on the future | |
82 * returned by [future]. This callback will only be invoked if the future | |
83 * is canceled before being completed. | |
84 */ | |
85 final CancelHandler _onCancel; | |
86 | |
87 _CancelableCompleterFuture<T> _future; | |
88 | |
89 /** | |
90 * Create a CancelableCompleter that will invoke the given callback | |
91 * synchronously if its future is canceled. The callback will not be | |
92 * invoked if the future is completed before being canceled. | |
93 */ | |
94 CancelableCompleter(this._onCancel) { | |
95 _future = new _CancelableCompleterFuture<T>(this); | |
96 | |
97 // When the client completes the inner completer, we need to check whether | |
98 // the outer completer has been completed. If it has, then the operation | |
99 // was canceled before it finished, and it's too late to un-cancel it, so | |
100 // we just ignore the result from the inner completer. If it hasn't, then | |
101 // we simply pass along the result from the inner completer to the outer | |
102 // completer. | |
103 // | |
104 // Note that the reason it is safe for the inner completer to be | |
105 // synchronous is that we don't expose its future to client code, and we | |
106 // only use it to complete the outer completer (which is asynchronous). | |
107 _innerCompleter.future.then((T value) { | |
108 if (!_outerCompleter.isCompleted) { | |
109 _outerCompleter.complete(value); | |
110 } | |
111 }, onError: (Object error, StackTrace stackTrace) { | |
112 if (!_outerCompleter.isCompleted) { | |
113 _outerCompleter.completeError(error, stackTrace); | |
114 } | |
115 }); | |
116 } | |
117 | |
118 /** | |
119 * The [CancelableFuture] that will contain the result provided to this | |
120 * completer. | |
121 */ | |
122 @override | |
123 CancelableFuture<T> get future => _future; | |
124 | |
125 /** | |
126 * Whether the future has been completed. This is independent of whether | |
127 * the future has been canceled. | |
128 */ | |
129 @override | |
130 bool get isCompleted => _innerCompleter.isCompleted; | |
131 | |
132 /** | |
133 * Complete [future] with the supplied value. If the future has previously | |
134 * been canceled, this will have no effect on [future], however it will | |
135 * still set [isCompleted] to true. | |
136 */ | |
137 @override | |
138 void complete([value]) { | |
139 _innerCompleter.complete(value); | |
140 } | |
141 | |
142 /** | |
143 * Complete [future] with an error. If the future has previously been | |
144 * canceled, this will have no effect on [future], however it will still set | |
145 * [isCompleted] to true. | |
146 */ | |
147 @override | |
148 void completeError(Object error, [StackTrace stackTrace]) { | |
149 _innerCompleter.completeError(error, stackTrace); | |
150 } | |
151 | |
152 void _cancel() { | |
153 if (!_outerCompleter.isCompleted) { | |
154 _outerCompleter.completeError(new FutureCanceledError()); | |
155 _onCancel(); | |
156 } | |
157 } | |
158 } | |
159 | |
160 /** | |
161 * An object representing a delayed computation that can be canceled. | |
162 */ | |
163 abstract class CancelableFuture<T> implements Future<T> { | |
164 /** | |
165 * A CancelableFuture containing the result of calling [computation] | |
166 * asynchronously. Since the computation is started without delay, calling | |
167 * the future's cancel method will have no effect. | |
168 */ | |
169 factory CancelableFuture(computation()) => | |
170 new _WrappedFuture<T>(new Future<T>(computation)); | |
171 | |
172 /** | |
173 * A CancelableFuture containing the result of calling [computation] after | |
174 * [duration] has passed. | |
175 * | |
176 * TODO(paulberry): if the future is canceled before the duration has | |
177 * elapsed, the computation should not be performed. | |
178 */ | |
179 factory CancelableFuture.delayed(Duration duration, [computation()]) => | |
180 new _WrappedFuture<T>(new Future<T>.delayed(duration, computation)); | |
181 | |
182 /** | |
183 * A CancelableFuture that completes with error. Since the future is | |
184 * completed without delay, calling the future's cancel method will have no | |
185 * effect. | |
186 */ | |
187 factory CancelableFuture.error(Object error, [StackTrace stackTrace]) => | |
188 new _WrappedFuture<T>(new Future<T>.error(error, stackTrace)); | |
189 | |
190 /** | |
191 * A CancelableFuture containing the result of calling [computation] | |
192 * asynchronously with scheduleMicrotask. Since the computation is started | |
193 * without delay, calling the future's cancel method will have no effect. | |
194 */ | |
195 factory CancelableFuture.microtask(computation()) => | |
196 new _WrappedFuture<T>(new Future<T>.microtask(computation)); | |
197 | |
198 /** | |
199 * A CancelableFuture containing the result of immediately calling | |
200 * [computation]. Since the computation is started without delay, calling | |
201 * the future's cancel method will have no effect. | |
202 */ | |
203 factory CancelableFuture.sync(computation()) => | |
204 new _WrappedFuture<T>(new Future<T>.sync(computation)); | |
205 | |
206 /** | |
207 * A CancelableFuture whose value is available in the next event-loop | |
208 * iteration. Since the value is available without delay, calling the | |
209 * future's cancel method will have no effect. | |
210 */ | |
211 factory CancelableFuture.value([value]) => | |
212 new _WrappedFuture<T>(new Future<T>.value(value)); | |
213 | |
214 /** | |
215 * If the delayed computation has not yet completed, attempt to cancel it. | |
216 * Note that the cancellation is not always possible. If the computation | |
217 * could be canceled, the future is completed with a FutureCanceledError. | |
218 * Otherwise it will behave as though cancel() was not called. | |
219 * | |
220 * Note that attempting to cancel a future that has already completed will | |
221 * never succeed--futures that have already completed retain their final | |
222 * state forever. | |
223 */ | |
224 void cancel(); | |
225 } | |
226 | |
227 /** | |
228 * Error which is used to complete any [CancelableFuture] which has been | |
229 * successfully canceled by calling its 'cancel' method. | |
230 */ | |
231 class FutureCanceledError {} | |
232 | |
233 class _CancelableCompleterFuture<T> implements CancelableFuture<T> { | |
234 final CancelableCompleter<T> _completer; | |
235 | |
236 _CancelableCompleterFuture(this._completer); | |
237 | |
238 @override | |
239 Stream<T> asStream() { | |
240 // TODO(paulberry): Implement this in such a way that | |
241 // StreamSubscription.cancel() cancels the future. | |
242 return _completer._outerCompleter.future.asStream(); | |
243 } | |
244 | |
245 @override | |
246 void cancel() { | |
247 _completer._cancel(); | |
248 } | |
249 | |
250 @override | |
251 Future catchError(Function onError, {bool test(Object error)}) => | |
252 _completer._outerCompleter.future.catchError(onError, test: test); | |
253 | |
254 @override | |
255 Future then(onValue(T value), {Function onError}) => | |
256 _completer._outerCompleter.future.then(onValue, onError: onError); | |
257 | |
258 @override | |
259 Future timeout(Duration timeLimit, {onTimeout()}) { | |
260 // TODO(paulberry): Implement this in such a way that a timeout cancels | |
261 // the future. | |
262 return _completer._outerCompleter.future.timeout(timeLimit, | |
263 onTimeout: onTimeout); | |
264 } | |
265 | |
266 @override | |
267 Future<T> whenComplete(action()) => | |
268 _completer._outerCompleter.future.whenComplete(action); | |
269 } | |
270 | |
271 /** | |
272 * A CancelableFuture that wraps an ordinary Future. Attempting to cancel a | |
273 * _WrappedFuture has no effect. | |
274 */ | |
275 class _WrappedFuture<T> implements CancelableFuture<T> { | |
276 final Future<T> _future; | |
277 | |
278 _WrappedFuture(this._future); | |
279 | |
280 @override | |
281 Stream<T> asStream() => _future.asStream(); | |
282 | |
283 @override | |
284 void cancel() {} | |
285 | |
286 @override | |
287 Future catchError(Function onError, {bool test(Object error)}) => | |
288 _future.catchError(onError, test: test); | |
289 | |
290 @override | |
291 Future then(onValue(value), {Function onError}) => | |
292 _future.then(onValue, onError: onError); | |
293 | |
294 @override | |
295 Future timeout(Duration timeLimit, {onTimeout()}) => | |
296 _future.timeout(timeLimit, onTimeout: onTimeout); | |
297 | |
298 @override | |
299 Future<T> whenComplete(action()) => _future.whenComplete(action); | |
300 } | |
OLD | NEW |