You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源分析

  1. Completer未在Timer取消时触发:调用Timer.cancel()后,对应的Completer没有被complete(),导致threadA中的await一直阻塞,后续步骤无法执行。
  2. 缺少执行时间统计逻辑:原代码没有实现计时功能,无法输出预期的执行时长。
  3. 全局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');
}

关键修改说明

  1. Timer取消回调绑定:重写每个Timer的cancel方法,在取消时检查Completer状态,未完成则主动调用complete(),确保await逻辑能继续执行后续步骤。
  2. 局部化Completer:将Completer改为每个步骤的局部变量,避免全局状态带来的逻辑冲突。
  3. 加入计时逻辑:用Stopwatch统计整个流程的执行时间,满足预期输出的时长展示需求。
  4. 流程连续性保障:每个Timer被取消后,对应的Completer会立即完成,threadA不会因阻塞而中断后续步骤。

内容的提问来源于stack exchange,提问作者Miguel de Sousa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 14:24:17