1. Dart的事件循环
Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是微任务队列microtask queue,另一个叫做事件队列event queue。从图中可以发现,微任务队列的执行优先级高于事件队列。
如上图中所示,入口函数 main() 执行完后,消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务,事件任务执行完毕后程序便会退出,但是,在事件任务执行的过程中也可以插入新的微任务和事件任务,在这种情况下,整个线程的执行过程便是一直在循环,不会退出,而Flutter中,主线程的执行过程正是如此,永不终止。
在Dart中,所有的外部事件任务都在事件队列中,如IO、计时器、点击、以及绘制事件等,而微任务通常来源于Dart内部,并且微任务非常少,之所以如此,是因为微任务队列优先级高,如果微任务太多,执行时间总和就越久,事件队列任务的延迟也就越久,对于GUI应用来说最直观的表现就是比较卡,所以必须得保证微任务队列不会太长。在事件循环中,当某个任务发生异常并没有被捕获时,程序并不会退出,而直接导致的结果是当前任务的后续代码就不会被执行了,也就是说一个任务中的异常是不会影响其它任务执行的。
2. Dart的异步操作
Dart中的异步操作主要使用Future以及async、await。
- Future
模拟网络请求需要3秒钟的时间,之后返回数据,代码如下:
import "dart:io";
main(List<String> 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<String> args) {
print("main function start");
print(getNetworkData());
print("main function end");
}
Future<String> getNetworkData() {
return Future<String>(() {
sleep(Duration(seconds: 3));
return "network data";
});
}
打印结果:
main function start
Instance of 'Future<String>'
main function end
打印结果可见这次没有阻塞现象,有了Future之后,通过.then的回调获取请求结果,代码如下:
main(List<String> 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<String>'
main function end
// 3s后执行下面的代码
network data
如果调用过程中出现了异常,可以通过.catchError获取异常信息,代码如下:
import "dart:io";
main(List<String> args) {
print("main function start");
var future = getNetworkData();
future.then((value) {
print(value);
}).catchError((error) { // 捕获出现异常时的情况
print(error);
});
print(future);
print("main function end");
}
Future<String> getNetworkData() {
return Future<String>(() {
sleep(Duration(seconds: 3));
// 不再返回结果,而是出现异常
// return "network data";
throw Exception("网络请求出现错误");
});
}
打印结果:
main function start
Instance of 'Future<String>'
main function end
// 3s后没有拿到结果,但是我们捕获到了异常
Exception: 网络请求出现错误
Future.delayed(时间, 回调函数)
在延迟一定时间时执行回调函数,执行完回调函数后会执行then的回调,代码如下:
main(List<String> args) {
print("main function start");
Future.delayed(Duration(seconds: 3), () {
return "3秒后的信息";
}).then((value) {
print(value);
});
print("main function end");
}
- await、async
await、async可以让我们用同步的代码格式,去实现异步的调用过程,通常一个async的函数会返回一个Future,将下面代码改成异步:
import "dart:io";
main(List<String> 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)]
所以需要将将getNetworkData函数定义成async函数,只需要在函数的()后面加上async关键字即可,代码如下:
String getNetworkData() async {
var result = await Future.delayed(Duration(seconds: 3), () {
return "network data";
});
return "请求到的数据:" + result;
}
运行代码会依旧报错:
使用async标记的函数,必须返回一个Future对象,继续修改代码:
main(List<String> args) {
print("main function start");
getNetworkData().then((value){
print(value);
});
print(getNetworkData());
print("main function end");
}
Future<String> 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<String> args) {
scheduleMicrotask(() {
print("Hello Microtask");
});
}