Reputation: 7437
Lets say that in Dart/Flutter you have the following code:
void myOperation() {
// could be anything
print('you didn't cancel me!');
}
Notice that the operation itself is not asynchronous and is void -- does not return anything.
We want it to execute at some point in the future, but we also want to be able to cancel it (because a new operation has been requested that supersedes it).
I've started by doing this:
Future.delayed(Duration(seconds: 2), myOperation())
... but this is not cancellable.
How exactly could you schedule that "operation," but also make it cancelable?
I'm thinking... we could modify the code like so:
Future.delayed(Duration(seconds: 2), () {
if (youStillWantThisToExecute) {
print('you didn't cancel me!');
}
});
But that's not really very good because it depends on a "global" boolean... and so if the boolean gets flipped to false, no operations will complete, even the most recently requested, which is the one we want to complete.
It would be nicer if there were a way to create any number of instances of the operation and cancel them on an individual basis... or to have a unique id assigned to each operation, and then instead of having a boolean control whether or not to execute... to have a "mostRecentId" int or something which is checked prior to execution.
Anyways...
CancelableOperation seemed promising just from its name.
So, I looked at its documentation:
CancelableOperation.fromFuture(Future inner, {FutureOr onCancel()}) Creates a CancelableOperation wrapping inner. [...] factory
But honestly that just makes my poor head hurt oh so much.
I've consulted other articles, questions, and answers, but they are all part of some specific (and complex) context and there isn't a dirt simple example anywhere to be found.
Is there a way to make a delayed future cancellable by wrapping it in some other class?
Can someone more experienced please provide at least one simple, complete, verified example that compiles in DartPad?
Thanks.
Upvotes: 5
Views: 4677
Reputation: 89965
You cannot cancel an existing Future
. If you do:
Future.delayed(
Duration(seconds: 2),
() {
print('hello');
},
);
as long as the process runs (and is processing its event queue) for at least 2 seconds, the Future
eventually will execute and print 'hello'
.
At best you can cause one of the Future
's completion callbacks to fire prematurely so that callers can treat the operation as cancelled or failed, which is what CancelableOperation
, et al. do.
Based on your updated question, which now asks specifically about delayed Future
s, you instead should consider using a Timer
, which is cancelable. (However, unlike a Future
, callers cannot directly wait on a Timer
. If that matters to you, you would need to create a Completer
, have callers wait on the Completer
's Future
, and let the Timer
's callback complete it.)
Upvotes: 1
Reputation: 4652
Use Timer:
var t = Timer(Duration(seconds: 400), () async {
client.close(force: true);
});
...
t.cancel();
Upvotes: 4
Reputation: 2529
Using CancalableOperation
will not stop print('hello');
from executing even if you cancel. What it does is canceling(discarding) the result(void in your case). I will give you 2 examples using CancalableOperation
and CancalableFuture
.
CancelableOperation
example final delayedFuture = Future.delayed(
Duration(seconds: 2),
() {
return 'hello';
},
);
final cancellableOperation = CancelableOperation.fromFuture(
delayedFuture,
onCancel: () => {print('onCancel')},
);
cancellableOperation.value.then((value) => {
// Handle the future completion here
print('then: $value'),
});
cancellableOperation.value.whenComplete(() => {
print('onDone'),
});
cancellableOperation.cancel(); // <- commment this if you want to complete
CancelableFuture
example final delayedFuture = ...;
final cancalableFuture = CancelableFuture<String>(
future: delayedFuture,
onComplete: (result) {
// Use the result from the future to do stuff
print(result);
},
);
cancalableFuture.cancel(); // <- commment this if you want to complete
CancelableFuture
implementationclass CancelableFuture<T> {
bool _cancelled = false;
CancelableFuture({
@required Future<dynamic> future,
@required void Function(T) onComplete,
}) {
future.then((value) {
if (!_cancelled) onComplete(value);
});
}
void cancel() {
_cancelled = true;
}
}
Upvotes: 2