I have these scenarios where I need to attempt something for some time or so many times, but not for too long. The .timeout property of the Future looked like a natural choice. What I learned is that the original Future keeps on going even after it is timed out e.g.:
Future<void> trySomething() async {
while (true) {
print('trying something...');
bool successful = false; // exit if done
if (successful) {
return;
}
await Future.delayed(const Duration(seconds: 1));
}
}
trySomething().timeout(const Duration(seconds: 10), onTimeout: () {
print('try something else...');
});
above code produces following output:
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): try something else...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
My not-so-elegant work around was to use external flag variable to indicate to the process inside the main future that its services are no longer required:
bool youAreTooLate = false;
Future<void> trySomething() async {
while (true) {
if (youAreTooLate) {
return;
}
print('trying something...');
bool successful = false; // exit if done
if (successful) {
return;
}
await Future.delayed(const Duration(seconds: 1));
}
}
trySomething().timeout(const Duration(seconds: 10), onTimeout: () {
youAreTooLate = true;
print('try something else...');
});
This produces the expected result below, but doesn't make me all that warm and fuzzy inside.
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): try something else...
Is there a better way to handle this situation?
If you're just trying to do something repeatedly with a delay, you can use a Timer to achieve this while maintaining the ability to cancel it.
final timer = Timer.periodic(const Duration(seconds: 1), (t) {
// Do repeated work here.
if (shouldCancel) {
// This is the 'timer' object.
t.cancel();
}
});
Future.delayed(const Duration(seconds: 10)).then((_) => timer.cancel());
If you're looking to actually wait for the work inside the timer to complete before retrying, you can do something like this:
Timer timer;
doWork() async {
// Do your work here.
// ...
if (retry) {
timer = Timer(const Duration(seconds: 1), doWork);
}
}
Future.delayed(const Duration(seconds: 10)).then((_) => timer?.cancel());
This should work, but if you already have an uncompleted Future there's no way to actually cancel it.
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