Dart中如何取消Future?该场景是否有替代方案?
在Dart中取消Future及场景替代方案
一、Dart原生Future能取消吗?
不能。Dart的原生Future一旦启动就会执行到完成,没有内置的取消机制——你只能选择忽略它的结果,没法中途中断正在运行的计算任务。
二、你的场景现有方案分析
你现在用的token标记方案是可行的:每次触发新请求时更新token,计算完成后对比当前token和任务启动时的token,不一致就丢弃结果。但这个方案的问题是:之前的计算任务还是会完整跑完,只是结果不用,会浪费CPU资源。
三、更优的替代方案
1. 使用async包的CancelableOperation
这个包提供了可取消的操作,能在新请求触发时,直接取消之前的任务(如果任务支持响应取消信号的话),避免无效计算。
先添加依赖:async: ^2.11.0(可使用最新版本)
改造你的代码示例:
import 'package:async/async.dart'; import 'package:flutter/foundation.dart'; class A extends ChangeNotifier { int _token = 0; Data? _data; Data? get data => _data; CancelableOperation? _currentOperation; Future<void> foo() async { // 取消之前的任务 _currentOperation?.cancel(); _token += 1; final token = _token; _currentOperation = CancelableOperation.fromFuture( ReallyLongCompute(), onCancel: () { // 如果计算任务支持主动终止,可以在这里加逻辑,比如设置取消标记 }, ); try { final data = await _currentOperation?.value; if (_token == token && data != null) { _data = data; notifyListeners(); } } on CanceledError { // 任务被取消,直接忽略 } finally { _currentOperation = null; } } } void ButtonOnClick() async { final a = Provider.of<A>(context, listen: false); await a.foo(); }
如果你的ReallyLongCompute是可中断的计算(比如循环处理),可以给它加取消检查:
Future<Data> ReallyLongCompute({CancelableOperation? operation}) async { Data result = Data(); for (int i = 0; i < 100000; i++) { // 检查是否被取消,是的话提前终止 if (operation?.isCanceled ?? false) { throw CanceledError(); } // 执行计算逻辑 result.update(i); } return result; }
2. 使用Stream的switchMap
如果请求是连续触发的,用Stream配合switchMap可以自动丢弃之前的请求,只处理最新的:
class A extends ChangeNotifier { Data? _data; Data? get data => _data; final _requestController = StreamController<void>(); A() { _requestController.stream .switchMap((_) async* { yield await ReallyLongCompute(); }) .listen((data) { _data = data; notifyListeners(); }); } void foo() { _requestController.add(null); } @override void dispose() { _requestController.close(); super.dispose(); } }
每次调用foo()时,switchMap会取消之前的订阅,旧任务即使跑完也不会触发回调,只处理最新的计算结果。
3. 自定义取消标记
如果不想依赖第三方包,可以自己实现简单的取消标记,在计算任务中定期检查:
class CancelToken { bool _isCanceled = false; bool get isCanceled => _isCanceled; void cancel() => _isCanceled = true; } class A extends ChangeNotifier { Data? _data; Data? get data => _data; CancelToken? _currentToken; Future<void> foo() async { // 取消之前的任务 _currentToken?.cancel(); final newToken = CancelToken(); _currentToken = newToken; final data = await ReallyLongCompute(token: newToken); // 检查是否被取消 if (!newToken.isCanceled) { _data = data; notifyListeners(); } } } Future<Data> ReallyLongCompute({required CancelToken token}) async { Data result = Data(); for (int i = 0; i < 100000; i++) { if (token.isCanceled) break; result.update(i); } return result; }
总结
- 原生Future不能取消,只能忽略结果;
- 你的token方案是基础实现,但会浪费计算资源;
- 如果要真正中断计算,推荐用
CancelableOperation或自定义取消标记,在计算任务中加入取消检查; - 如果只需要丢弃结果,用
Stream.switchMap更简洁。
内容的提问来源于stack exchange,提问作者Fractale
相关产品推荐
相关产品推荐

