Flulutter中的异步(三)

15 阅读2分钟

Flulutter中的异步(三)

Stream流处理

Stream流主要用来弥补future的短板,一个future最终只能有一个结果即一次请求对应的只能有一个相应,严格的一对一模式使得future不能处理一次请求多次响应的情况,且由于响应的不确定性,机体并不需要处于同步等待的方式,像这种异步事件序列就会被称为stream流

读取文件

File 对象可以通过 readAsString 异步方法读取文件内容,返回 Future<String> 类型对象。像这种情况由于只有一次响应的机会,那么当通过then回调的时候,该方法会返回文件中的所有字符或者返回异常。而很多时候我们往往不需要返回整个文档,或着在异常时能够返回部分文档,这个时候future就无法做到需要用到stream流了。 由于系统加载文件是一块一块读取的,在每加载一部分文件的时候都要通知机体如何处理。 在stream中有两个角色:发布者和订阅者,其中发布者是真正在处理任务的人,例如服务器。而订阅者是发送请求的机体,订阅者本身并不参与到执行过程中,可以监听通知来获取需要的结果数据。 代码处理中 Stream 对象使用 listen 方法 监听通知 ,该方法的第一入参是回调函数,每次通知时都会被触发。回调函数的参数类型是 Stream 的泛型,表示此次通知时携带的结果数据。

StreamSubscription<T> listen(void onData(T event)?,
    {Function? onError, void onDone()?, bool? cancelOnError});

例如下面就是对文件读取的一个监听例子,每次读取都会计算当前的读取进度。

void readFile() async {
  File file = File(path.join(Directory.current.path, "assets", "Jane Eyre.txt"));
  print("开始读取 Jane Eyre.txt ");
  fileLength = await file.length();
  Stream<List<int>> stream = file.openRead();
  stream.listen(_onData,onDone: _onDone);
}

void _onData(List<int> bytes) {
  counter += bytes.length;
  double progress = counter * 100 / fileLength;
  DateTime time = DateTime.now();
  String timeStr = "[${time.hour}:${time.minute}:${time.second}:${time.millisecond}]";
  print(timeStr + "=" * (progress ~/ 2) + '[${progress.toStringAsFixed(2)}%]');
}

void _onDone() {
  print("读取 Jane Eyre.txt 结束");
}

filelength用于收集整个文档的长度用于计算后续的进度条onData函数则是监听对象,每读取完一个块就会返回当前读取的byte数并计算占总文件大小的百分比当任务最终完成时会触发_onDone函数表示任务已经完成。

StreamSubscription

Stream#listen 方法监听后,会返回一个 StreamSubscription 对象,表示此次对流的订阅。该订阅可以被暂停(pause)、恢复(resume)以及取消(cancel
需要注意的是当订阅被取消时,任务并不会被认为完成,因此上文的_onDone函数并不会被触发。