Is it possible to perform async steps with Timer
instead of Future.delayed
? The main reason is the availability of cancel()
method in the Timer
class, which allows me to cancel a running timer when a widget is disposed in Flutter.
import 'dart:async';
const kFirstStepDuration = Duration(seconds: 1);
const kSecondStepDuration = Duration(seconds: 1);
const kThirdStepDuration = Duration(seconds: 1);
void main() async{
await Future.delayed(kFirstStepDuration);
print('first step');
await Future.delayed(kSecondStepDuration);
print('second step');
await Future.delayed(kThirdStepDuration);
print('end');
}
Using the Timer following Richard Heap answer, I would like to cancel the steps whenever its possible, but the code below prompts:
first timer to begin
cancel first
cancel second
cancel third
My expectation was to prompt:
first timer to begin
cancel first
second timer to begin
cancel second
third timer to begin
cancel third
executed in 0:00:00.06000
import 'dart:async';
const kFirstStepDuration = Duration(seconds: 1);
const kSecondStepDuration = Duration(seconds: 1);
const kThirdStepDuration = Duration(seconds: 1);
Timer? firstTimer;
Timer? secondTimer;
Timer? thirdTimer;
final firstCompleter = Completer();
final secondCompleter = Completer();
final thirdCompleter = Completer();
void main() {
threadA();
threadB();
}
void threadA() async {
print('first timer to begin');
firstTimer = Timer(kFirstStepDuration, ()=> firstCompleter.complete());
await firstCompleter.future;
print('second timer to begin');
secondTimer = Timer(kSecondStepDuration, ()=> secondCompleter.complete());
await secondCompleter.future;
print('third timer to begin');
thirdTimer = Timer(kThirdStepDuration, ()=> thirdCompleter.complete());
await thirdCompleter.future;
}
void threadB() async {
await Future.delayed(Duration(milliseconds: 20));
firstTimer?.cancel();
print('cancel first');
await Future.delayed(Duration(milliseconds: 20));
secondTimer?.cancel();
print('cancel second');
await Future.delayed(Duration(milliseconds: 20));
thirdTimer?.cancel();
print('cancel third');
}
CodePudding user response:
await
a future that you complete
in the timer's callback.
print('starting');
final completer = Completer();
final t = Timer(Duration(seconds: 3), () => completer.complete());
await completer.future;
print('done');
The problem with this is the completer won't complete if the timer is cancelled, as the callback is never called. So you have to cancel the timer and complete the completer.
Wouldn't it be easier to check the widget is still mounted after the Future.delayed
?
EDIT
Following the update to your question, it seems you didn't the 'problem' above. You could encapsulate all the functionality into a class like:
class PointlessTimer {
PointlessTimer(Duration d) {
_timer = Timer(d, () => _completer.complete());
}
late final Timer _timer;
final _completer = Completer();
Future get future => _completer.future;
void cancel() {
_timer.cancel();
_completer.complete();
}
}
and use it like this:
void main() async {
print('starting');
final pointless = PointlessTimer(Duration(seconds: 3));
Timer(Duration(seconds: 2), () => pointless.cancel());
await pointless.future;
print('done');
}
CodePudding user response:
Following Richard Heap answer:
/// works as a Future.delayed. Returns a timer to be canceled.
/// useful when disposing widgets before a timer completion.
/// necessary when testing widgets that disposes before the timer completion.
Future<Timer> _delayed(Duration duration) async {
final completer = Completer();
final timer = Timer(duration, () {
completer.complete();
});
await completer.future;
return timer;
}