Dart
是一个在单线程中运行的程序,如果遇到一个耗时的操作,程序将会被冻结。为了避免这种情况,可以使用一个异步操作使程序在等待耗时操作时完成时继续处理其他操作。在Dart
中,可以使用Future
对象来表示异步操作的结果。
1、Dart的消息循环机制
Dart
中有一个事件循环(Event Loop) 来执行我们的代码,里面存在一个事件队列(Event Queue),事件循环不断从事件队列中取出事件执行。
1.1、微任务队列
- 微任务队列的优先级要高于事件队列
- 事件循环优先执行微任务队列中的任务,再执行事件队列中的任务
- 所有的外部事件任务都在事件队列中,如:IO、计时器、点击、绘制事件
- 微任务通常来源于
Dart
内部,并且微任务非常少
在Dart
的单线程中,代码执行顺序是怎样的呢?
1、Dart
的入口函数是main
函数,所以main
函数中的代码会优先执行
2、main
函数执行完成后,会启动一个事件循环(Event Loop),启动后开始执行队列中的任务
3、首先,会按照先进先出的顺序,执行 微任务队列(Microtask Quene) 中的所有任务
4、其次,会按照先进先出的顺序,执行 事件队列(Event Quene) 中的所有任务
1.2、创建微任务
在开发中,如果我们有一个任务不希望它放在Event Quene
中排队,那么就可以创建一个微任务
我们可以通过dart
中async
下的scheduleMicrotask
来创建一个任务
import "dart:async";
main(List<String> args) {
scheduleMicrotask(() {
print("Hello Microtask");
});
}
1.2.1、Future 的代码是加入微任务队列还是事件队列?
Future
中通常有两个函数执行体:
Future
构造函数传入的函数体then
函数的函数体
那么它们是加入到什么队列中呢?
Future
的构造函数传入的函数体放事件队列中then
的函数体分以下几种情况:
1、Future
没有执行完成,then
函数会被添加到Futue
函数执行体后
2、Future
执行完成后执行then
,then
函数体被放到微任务队列,当前Future
执行完成后再执行微任务队列
3、如果Future
是链式调用,则当前then
函数体未执行完成,下一个then
不会执行
2、async/await
Flutter
是一个单线程并且跑着一个event loop
,因此不必担心线程管理。如果你正在做I/O
操作,可以安全地使用async/await
来完成。如果需要让CPU
执行繁忙的计算密集型任务,就需要使用Isolate
来避免阻塞event loop
。
对于I/O
操作,通过关键字async
把方法声明为异步方法,然后通过await
关键字等待该异步方法执行完成:
loadData() async {
String url = "https://xxxx";
http.Response response = await http.get(url);
setState(() {
widgets = json.decode(response.body);
});
}
3、Isolate
Isolate
是分离的运行线程,并且不和主线程的内存堆共享内存。这意味着你不能访问主线程中的变量,或者使用setState()
来更新UI
,Isolate
不能共享内存,每个lsolate
都有自己的Event Loop
与Quene
。
下面是简单的Isolate的简单用法,把数据返回给主线程更新UI:
import 'dart:isolate';
...
loadData() async {
//打开ReceivePort以接收传入的消息
ReceivePort receiverPort = ReceivePort();
//创建并生成与当前Isolate共享相同代码的Isolate
await Isolate.spawn(dataLoader,receiverPort.sendPort);
//流的第一个元素
SendPort sendPort = await receiverPort.first;
//流的第一个元素被收到后监听会关闭,所以需要新打开一个ReceivePort以接收传入的消息
ReceivePort response = ReceivePort();
//通过此发送端口向其对应的‘ReceivePort’发送异步[消息],这个消息是指要发送的参数
sendPort.send(["https://xxxx", response.sendPort]);
//获取端口发来的数据
List msg = await response.first;
//更新UI
setState(() {
widgets = msg;
});
}
//Isolate的入口函数,该函数会在新的Isolate中调用, Isolate.spawn的message参数会作为调用它的唯一参数
static dataLoader(SendPort sendPort) async {
//打开ReceivePort以接收传入的消息
ReceivePort port = ReceivePort();
//通知其他的isolates,本isolate所监听的端口
sendPort.send(port.sendPort);
//获取其他端口发送的异步消息msg
await for (var msg in port) {
//等价于List msg = await port.first
String data = msg[0];
SendPort replyTo = msg[1];
String dataURL = data;
http.Response response = await http.get(dataURL);
//其对应的“ReceivePort”发出解析出来的Json数据
replyTo.send(json.decode(response.body));
}
}
dataLoader()
是一个运行于自己独立线程上的Isolate
。在Isolate
里,你可以执行CPU
密集型任务(如解析庞大的json
,解析json
也是很耗时的),或是计算密集型的数学操作,如加解密或信号处理等。