前言
Dart作为Flutter的开发语言,了解Dart的机制是必可少的。本篇文章就介绍一下Dart的异步操作与事件循环机制。
异步操作我们都知道在开发过程中,如果有耗时操作,我们一般都会使用异步任务解决,以防主线程卡顿。
事件循环是Dart中处理事件的一种机制。Flutter中就是通过事件循环来驱动程序的运行,这点与Android中的Handler有点类似。
Dart的事件循环机制
Dart语言是单线程模型的语言。这也就意味着Dart在同一时刻只能执行一个操作,其他操作在这个操作之后执行。那么Dart的其他操作如何执行呢?在Dart中通过Event Loop来驱动事件的执行。
- Dart程序启动时会创建两个队列,分别是MicroTak队列(微服务队列)和Event队列(事件队列);
- 然后执行main()方法;
- 最后启动事件循环;
上面的图片描述了Dart的事件循环机制。可以看到,Dart执行事件的流程是:
- 执行main();
- 执行MicroTask队列中事件;
- 执行事件队列中的事件;
可以看到,main()方法的优先级最高,执行结束后,才执行两个队列中事件。其中MicroTask队列的优先级又高于Event队列的优先级。
MicroTask队列和Event队列
MicroTask 队列适用于非常简短且需要异步执行的内部动作或者想要在Event执行前做某些操作,因为在MicroTask执行完以后,还需要执行Event队列中事件。
在使用Dart时异步事件应该优先考虑使用Event队列。
Dart中的异步
Future
Future 是一个异步执行并且在未来的某一个时刻完成(或失败)的任务。每一个Future都会执行一个事件并且将事件加入到Event队列中去。
factory Future(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
Timer.run(() {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
从上面Future的构造函数代码可以看到,通过Timer的run()方法将运算过程发送到事件队列中去,然后通过_complete()方法获取了提交给Future的运算结果,最终返回一个Future对象。
void main(){
print('Before the Future');
Future((){
print('Running the Future');
}).then((_){
print('Future is complete');
});
print('After the Future');
Future<String>.microtask(() {
return "microtask";
}).then((String value) {
print(value);
});
}
以上代码的运行结果是:
Before the Future
After the Future
microtask
Running the Future
Future is complete
可以看到,这里先执行的是main()方法中的打印信息,然后打印了microtask,最后才执行了Future。这里也可以证明Future是在事件队列中执行的。
async、await关键字
async与await关键字配合使用可以更加优雅的处理dart中的异步。对于有async修饰的函数来说,它会同步执行函数中的代码,直到遇到第一个await关键字,然后将await修饰的代码提交到事件队列中处理,在await修饰的代码执行完毕后会立即执行剩下的代码,而剩下的代码将在微任务队列中执行。
void methodAsync() async {
sleep(Duration(seconds: 1));
Future<String>(() {
return "future 1";
}).then((String value) {
print(value);
});
print("async start");
await microTaskAndEvent(); //输出一个微任务打印 microtask 以及一个事件队列任务打印 futrue
print("async end");
}
void main() {
methodAsync();
print("Hello World");
}
以上代码的输出结果是:
async start
Hello World
microtask
async end
future 1
future
从打印结果可以看出,
- 这里同步执行methodAsync()方法,即使在睡眠1s的情况下,也首先输出了async start;然后输出Hello World;
- 接下来执行了一个Future1,然后执行await关键字后面的函数;
- microTaskAndEvent()方法包括一个微任务和事件。这里可以看到打印了microtask接着打印了async end,这里可以证明async在await关键字之后的代码是放到微任务中执行的。
- 最后打印了事件队列中的任务。
Isolate
Isolate从名称上理解是独立的意思。在dart中,Isolate有着自己的内存并且不存在通过共享内存的方式实现Isolate之间的通信,同时Isolate是单线程的计算过程。
Isolate的特点与使用:
- Isolate有着自己的内存并且不存在通过共享内存的方式实现Isolate之间的通信;
- 每个Isolate都有自己的事件循环;
- 在Isolate中是通过Port进行通信的。Port分为两种一种是receive port用于接收消息。另外一种是send port用于发送消息;
- 使用Isolate时,可以通过spwan()方法启动一个Isolate
接下来通过一个例子展示Isolate的通信以及创建过程。
main() async {
var receivePort = new ReceivePort();
/**
* 初始化另外一个Isolate,
* 将main isolate的send port发送到child isolate中,
* 用于向main isolate发送信息
*/
await Isolate.spawn(echo, receivePort.sendPort);
// child isolate的第一个信息作为child isolate的send port,由于向child isolate发送信息
var sendPort = await receivePort.first;
var msg = await sendReceive(sendPort, "hello");
print('received $msg');
msg = await sendReceive(sendPort, "close");
print('received $msg');
}
// child isolate的入口
echo(SendPort sendPort) async {
// 打开一个接收端口
var port = new ReceivePort();
//监听接收的信息
port.listen((dynamic msg) {
print("child receive $msg");
var data = msg[0];
SendPort replyTo = msg[1];
replyTo.send(data);
if (data == "close") port.close(); //关闭接收端口
});
// 向其他的isolate通知child isolate的发送端口.
sendPort.send(port.sendPort);
}
//发送信息
Future sendReceive(SendPort port, msg) {
ReceivePort response = new ReceivePort();
port.send([msg, response.sendPort]);
return response.first;
}
总结
以上就是关于Dart的异步以及事件循环原理。理解这些内容是必要的,因为在很多的开发场景都会用到异步,同时需要很好的理解Dart的事件处理机制。