简体   繁体   中英

create async await steps with Timer instead of Future.delayed(...)

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');
}

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');
}

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;
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM