Dart 应用程序有一个带有两个队列的事件循环 ------ 事件 队列 和微任务队列。
Dart 程序的执行流程始于主 isolate
的 main
函数,然后可以通过隔离体机制创建和管理其他隔离体。每个子隔离体都可以有自己的入口点函数, 也就是耗时任务的函数。
事件循环和队列
在客户端应用中,主 isolate 的事件队列内,可能会包含重绘的请求、点击的通知或者(计时器、Dart 隔离之间的消息)其他界面事件。例如,下图展示了包含四个事件的事件队列,队列会按照先进先出的模式处理事件。
如下图所示,在 main()
方法执行完毕后,事件队列中的处理才开始,此时处理的是第一个重绘的事件。而后主 isolate 会处理点击事件,接着再处理另一个重绘事件。
下面这张图, 基本上可以表示事件模型。
当事件循环执行微任务队列中的任务时,事件队列会被卡住:应用程序无法绘制图形、处理鼠标单击、对 I/O 做出反应等。如下图所示。
在一个客户端应用中,耗时过长的同步操作,通常会导致 卡顿的动画。而最糟糕的是,应用界面可能完全失去响应。 这种情况, 我们可以考虑上一篇张讲的开启一个子isolate
, 来处理耗时过长的同步任务。
main函数触发流程
main
函数的触发是由_RawReceivePortImpl#_handleMessage
方法触发的:
_startMainIsolate
typescript
/**
* Takes the real entry point as argument and schedules it to run in the message
* queue.
*/
@pragma("vm:entry-point", "call")
void _startMainIsolate(Function entryPoint, List<String>? args) {
_delayEntrypointInvocation(entryPoint, args, null, true);
}
程序的实际入口点函数 _startMainIsolate
标记为 Dart VM 的入口点,并且通过调用 _delayEntrypointInvocation
函数来延迟执行入口点函数。这是 Dart VM 启动和执行 Dart 程序的一部分。
_delayEntrypointInvocation
scss
void _delayEntrypointInvocation(Function entryPoint, List<String>? args,
Object? message, bool allowZeroOneOrTwoArgs) {
final port = RawReceivePort();
port.handler = (_) { /// mark1 触发回调
port.close();
if (allowZeroOneOrTwoArgs) {
if (entryPoint is _BinaryFunction) {
(entryPoint as Function)(args, message);
} else if (entryPoint is _UnaryFunction) {
(entryPoint as Function)(args);
} else {
entryPoint();
}
} else {
entryPoint(message);
}
};
port.sendPort.send(null);
}
处理入口函数的延迟调用的。
-
创建一个
RawReceivePort
对象,这是 Dart 中用于在隔离体之间进行消息通信的一种机制。 -
为这个
RawReceivePort
对象设置一个事件处理器(handler),当接收到消息时,这个处理器会被触发。 -
在事件处理器中,首先关闭了这个
RawReceivePort
,以确保它只能被触发一次。
main
函数的触发也涉及 消息通知机制
,_startIsolate
触发 _delayEntrypointInvocation
方法,其中创建 RawReceivePort
接收端看对象,并为 handler
赋值。
_startIsolate
javascript
void _startIsolate(
Function entryPoint, List<String>? args, Object? message, bool isSpawnUri) {
_delayEntrypointInvocation(entryPoint, args, message, isSpawnUri);
}
触发main方法回调
也就是说收到消息,触发 _RawReceivePortImpl#_handleMessage
时,执行的 handler
就是 mark1
所示的函数。 tag2
的 entryPoint()
方法就是 main
方法。