Dart学习笔记08-异步

Dart是单线程的,Dart基于单线程加事件循环来完成耗时操作的处理,下图为Dart官方提供的Dart运行原理图。
Dart运行原理

1. Dart的事件循环

Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是微任务队列microtask queue,另一个叫做事件队列event queue。从图中可以发现,微任务队列的执行优先级高于事件队列。
如上图中所示,入口函数 main() 执行完后,消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务,事件任务执行完毕后程序便会退出,但是,在事件任务执行的过程中也可以插入新的微任务和事件任务,在这种情况下,整个线程的执行过程便是一直在循环,不会退出,而Flutter中,主线程的执行过程正是如此,永不终止。
在Dart中,所有的外部事件任务都在事件队列中,如IO、计时器、点击、以及绘制事件等,而微任务通常来源于Dart内部,并且微任务非常少,之所以如此,是因为微任务队列优先级高,如果微任务太多,执行时间总和就越久,事件队列任务的延迟也就越久,对于GUI应用来说最直观的表现就是比较卡,所以必须得保证微任务队列不会太长。在事件循环中,当某个任务发生异常并没有被捕获时,程序并不会退出,而直接导致的结果是当前任务的后续代码就不会被执行了,也就是说一个任务中的异常是不会影响其它任务执行的。

2. Dart的异步操作

Dart中的异步操作主要使用Future以及asyncawait

  • Future
    模拟网络请求需要3秒钟的时间,之后返回数据,代码如下:
import "dart:io";

main(List args) {
  print("main function start");
  print(getNetworkData());
  print("main function end");
}

String getNetworkData() {
  sleep(Duration(seconds: 3));
  return "network data";
}

打印结果:

main function start
// 等待3秒
network data
main function end

这段代码getNetworkData会阻塞main函数的执行,Dart中可以使用Future创建了一个实例,进行异步操作,代码如下:

import "dart:io";

main(List args) {
  print("main function start");
  print(getNetworkData());
  print("main function end");
}

Future getNetworkData() {
  return Future(() {
    sleep(Duration(seconds: 3));
    return "network data";
  });
}

打印结果:

main function start
Instance of 'Future'
main function end

打印结果可见这次没有阻塞现象,有了Future之后,通过.then的回调获取请求结果,代码如下:

main(List args) {
  print("main function start");
  // 使用变量接收getNetworkData返回的future
  var future = getNetworkData();
  // 当future实例有返回结果时,会自动回调then中传入的函数
  // 该函数会被放入到事件循环中,被执行
  future.then((value) {
    print(value);
  });
  print(future);
  print("main function end");
}

打印结果:

main function start
Instance of 'Future'
main function end
// 3s后执行下面的代码
network data

如果调用过程中出现了异常,可以通过.catchError获取异常信息,代码如下:

import "dart:io";

main(List args) {
  print("main function start");
  var future = getNetworkData();
  future.then((value) {
    print(value);
  }).catchError((error) { // 捕获出现异常时的情况
    print(error);
  });
  print(future);
  print("main function end");
}

Future getNetworkData() {
  return Future(() {
    sleep(Duration(seconds: 3));
    // 不再返回结果,而是出现异常
    // return "network data";
    throw Exception("网络请求出现错误");
  });
}

打印结果:

main function start
Instance of 'Future'
main function end
// 3s后没有拿到结果,但是我们捕获到了异常
Exception: 网络请求出现错误

Future.delayed(时间, 回调函数)
在延迟一定时间时执行回调函数,执行完回调函数后会执行then的回调,代码如下:

main(List args) {
  print("main function start");

  Future.delayed(Duration(seconds: 3), () {
    return "3秒后的信息";
  }).then((value) {
    print(value);
  });

  print("main function end");
}
  • await、async
    awaitasync可以让我们用同步的代码格式,去实现异步的调用过程,通常一个async的函数会返回一个Future,将下面代码改成异步:
import "dart:io";

main(List args) {
  print("main function start");
  print(getNetworkData());
  print("main function end");
}

String getNetworkData() {
  var result = Future.delayed(Duration(seconds: 3), () {
    return "network data";
  });

  return  "请求到的数据:" + result;
}

在Future.delayed函数前加了一个await,会发现代码报错:[图片上传中...(错误信息.png-75d3c9-1597927310305-0)]

错误信息.png

所以需要将将getNetworkData函数定义成async函数,只需要在函数的()后面加上async关键字即可,代码如下:

String getNetworkData() async {
  var result = await Future.delayed(Duration(seconds: 3), () {
    return "network data";
  });

  return  "请求到的数据:" + result;
}

运行代码会依旧报错:

错误信息.png

使用async标记的函数,必须返回一个Future对象,继续修改代码:

main(List args) {
  print("main function start");
  getNetworkData().then((value){
      print(value);
  });
  print(getNetworkData());
  print("main function end");
}

Future getNetworkData() async {
  var result = await Future.delayed(Duration(seconds: 3), () {
    return 'network data';
  });

  return  "请求到的数据:" + result;
}

ps:返回的时候并不需要包装一个Future,直接返回即可,返回值会默认被包装在一个Future

  • Stream
    Stream也是用于接收异步事件数据,和Future不同的是,它可以接收多个异步操作的结果。Stream常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。代码示例:
Stream.fromFutures([
    // 1s后返回结果
    Future.delayed(Duration(seconds: 1), () {
      return 'task 1';
    }),

    // 抛出一个异常
    Future.delayed(Duration(seconds: 2), () {
      throw AssertionError('Error');
    }),

    // 3秒后返回结果
    Future.delayed(Duration(seconds: 3), () {
      return 'task 3';
    })
  ]).listen((data) {
    print(data);
  }, onError: (e) {
    print(e.message);
  }, onDone: () {});

输出结果如下:

task 1
Error
task 3

3. 如何创建微任务

我们可以通过dart中async下的scheduleMicrotask来创建一个微任务,代码示例:

import "dart:async";

main(List args) {
  scheduleMicrotask(() {
    print("Hello Microtask");
  });
}

你可能感兴趣的:(Dart学习笔记08-异步)