In a Dart UI, I have a button [submit] to launch a long async request. The [submit] handler returns a Future. Next, the button [submit] is replaced by a button [cancel] to allow the cancellation of the whole operation. In the [cancel] handler, I would like to cancel the long operation. How can I cancel the Future returned by the submit handler? I found no method to do that.
Many of you know that you can't cancel a Future in Dart, but you can cancel a subscription to a Stream. So one way you could handle this situation is to rewrite getData() to return a Stream instead.
To prevent multiple awaits, chaining futures in . then(), you can simply use Future. wait([]) that returns an array of results you were waiting for. If any of those Futures within that array fails, Future.
A future (lower case “f”) is an instance of the Future (capitalized “F”) class. A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed. Note: Uncompleted is a Dart term referring to the state of a future before it has produced a value.
You can use CancelableOperation or CancelableCompleter to cancel a future. See below the 2 versions:
CancelableOperation
(included in a test so you can try it yourself):test("CancelableOperation with future", () async { var cancellableOperation = CancelableOperation.fromFuture( Future.value('future result'), onCancel: () => {debugPrint('onCancel')}, ); // cancellableOperation.cancel(); // uncomment this to test cancellation cancellableOperation.value.then((value) => { debugPrint('then: $value'), }); cancellableOperation.value.whenComplete(() => { debugPrint('onDone'), }); });
test("CancelableOperation with stream", () async { var cancellableOperation = CancelableOperation.fromFuture( Future.value('future result'), onCancel: () => {debugPrint('onCancel')}, ); // cancellableOperation.cancel(); // uncomment this to test cancellation cancellableOperation.asStream().listen( (value) => { debugPrint('value: $value') }, onDone: () => { debugPrint('onDone') }, ); });
Both above tests will output:
then: future result onDone
Now if we uncomment the cancellableOperation.cancel();
then both above tests will output:
onCancel
CancelableCompleter
(if you need more control)test("CancelableCompleter is cancelled", () async { CancelableCompleter completer = CancelableCompleter(onCancel: () { print('onCancel'); }); // completer.operation.cancel(); // uncomment this to test cancellation completer.complete(Future.value('future result')); print('isCanceled: ${completer.isCanceled}'); print('isCompleted: ${completer.isCompleted}'); completer.operation.value.then((value) => { print('then: $value'), }); completer.operation.value.whenComplete(() => { print('onDone'), }); });
Output:
isCanceled: false isCompleted: true then: future result onDone
Now if we uncomment the cancellableOperation.cancel();
we get output:
onCancel isCanceled: true isCompleted: true
Be aware that if you use await cancellableOperation.value
or await completer.operation
then the future will never return a result and it will await indefinitely if the operation was cancelled. This is because await cancellableOperation.value
is the same as writing cancellableOperation.value.then(...)
but then()
will never be called if the operation was cancelled.
Code gist
Future.delayed
A simple way is to use Timer
instead :)
Timer _timer; void _schedule() { _timer = Timer(Duration(seconds: 2), () { print('Do something after delay'); }); } @override void dispose() { super.dispose(); _timer?.cancel(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With