首先要明确的一点是, 在dart协程式的编程语言中, 是无法中断一个Future的执行的, 我们所能做的只能是取消一个async代码块的回调, 也就是说一段异步代码块执行后, 准备继续执行后续的回调代码时, 由于已经被取消, 而中止了整个过程.
由于在dart中有回调机制并能被中止的有Futhre.then()和Stream.listen(), 所以下面就有相应的实现方法。
方法1: 使用CancelableOperation
下面是一个异步获取数据, 当页面销毁时中断渲染的示例代码:
import 'package:async/async.dart';
void loadPageDatas() async {
CancelableOperation cancelTask = CancelableOperation.fromFuture(
Future(() async {
print('start query datas');
await Future.delayed(Duration(seconds: 6));
print('end query datas');
return "value";
}),
onCancel: () => print('disposed, cancel rendering.'),
);
cancelTask.value.then((val) {
print('to render tree: $val '); // 不能放在cancelTask的构造函数中, 需要通过 cancelTask.value.then进行添加.
});
Future.delayed(Duration(seconds: 3)).then((_) {
// when disposed
cancelTask.cancel();
});
}
有一些细节, 通过源码cancelable_operation.dart::CancelableCompleter()实现可以知道, 由于CancelableOperation是对Future的then进行了接管, 判断isCanceled标记以决定是否需要执行用户提供的then(), 但这个接管是在CancelableCompleter()构造完成后的 cancelTask.value中进行的,
所以在CancelableOperation.fromFuture()只提供不可中断的功能(如querying datas), 将可中断的功能(如rendering)放在构造完成后的.value.then中添加,
这是需要注意的,否则会出现即使cancel()后仍会执行的情况。
CancelableOperation 类的本质是有个 CancelableCompleter 类, 在其中完成了cancel的操作, 可以阅读源码.
方法2:使用Stream.
同样的示例代码, 很简单:
Future<String> getData() async {
print('into getData');
await Future.delayed(Duration(seconds: 5));
print('leave getData');
return "value";
}
void loadPageDatas() async {
StreamSubscription<String> dataSub =
getData().asStream().listen((String data) {
print('rendering data: $data');
});
Future.delayed(Duration(seconds: 3), () => dataSub.cancel());
}
代码比较清晰, 不多解释了。
参考来源:
https://dart.academy/how_cancel_future/
有个题外话,在使用Future.delayed(Duration(seconds: 3, milliseconds: 100))时,要小心精度问题,比如Duration(seconds: 3, milliseconds: 100)有可能比Duration(seconds: 3) 要先被执行,这牵涉到task的调度,还没有深究,有时间了再做研究。