如何用Timer替代Future.delayed实现可取消的异步步骤?
问题:Timer替代Future.delayed实现可取消异步步骤的异常修复
想要用Timer替代Future.delayed执行异步步骤,核心需求是利用Timer的cancel()方法在组件销毁时终止正在运行的计时器。但现有实现的输出不符合预期:
当前输出
first timer to begin cancel first cancel second cancel third
预期输出
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'); }
问题根源分析
- Completer未在Timer取消时触发:调用
Timer.cancel()后,对应的Completer没有被complete(),导致threadA中的await一直阻塞,后续步骤无法执行。 - 缺少执行时间统计逻辑:原代码没有实现计时功能,无法输出预期的执行时长。
- 全局Completer存在状态污染风险:全局声明的Completer如果不重置,可能导致重复使用时的逻辑异常。
修正后的代码
import 'dart:async'; const kFirstStepDuration = Duration(seconds: 1); const kSecondStepDuration = Duration(seconds: 1); const kThirdStepDuration = Duration(seconds: 1); Timer? firstTimer; Timer? secondTimer; Timer? thirdTimer; void main() { final stopwatch = Stopwatch()..start(); threadA(stopwatch); threadB(); } void threadA(Stopwatch stopwatch) async { // 第一步:绑定Timer取消回调,确保取消后Completer完成 final firstCompleter = Completer(); print('first timer to begin'); firstTimer = Timer(kFirstStepDuration, () => firstCompleter.complete()); // 重写cancel方法,取消时主动完成Completer firstTimer?.cancel = () { if (!firstCompleter.isCompleted) firstCompleter.complete(); }; await firstCompleter.future; // 第二步 final secondCompleter = Completer(); print('second timer to begin'); secondTimer = Timer(kSecondStepDuration, () => secondCompleter.complete()); secondTimer?.cancel = () { if (!secondCompleter.isCompleted) secondCompleter.complete(); }; await secondCompleter.future; // 第三步 final thirdCompleter = Completer(); print('third timer to begin'); thirdTimer = Timer(kThirdStepDuration, () => thirdCompleter.complete()); thirdTimer?.cancel = () { if (!thirdCompleter.isCompleted) thirdCompleter.complete(); }; await thirdCompleter.future; // 输出执行时长 stopwatch.stop(); print('executed in ${stopwatch.elapsed}'); } 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'); }
关键修改说明
- Timer取消回调绑定:重写每个Timer的
cancel方法,在取消时检查Completer状态,未完成则主动调用complete(),确保await逻辑能继续执行后续步骤。 - 局部化Completer:将Completer改为每个步骤的局部变量,避免全局状态带来的逻辑冲突。
- 加入计时逻辑:用
Stopwatch统计整个流程的执行时间,满足预期输出的时长展示需求。 - 流程连续性保障:每个Timer被取消后,对应的Completer会立即完成,
threadA不会因阻塞而中断后续步骤。
内容的提问来源于stack exchange,提问作者Miguel de Sousa
相关产品推荐
相关产品推荐

