首先我们要知道事件循环和异步之间的关系。在dart层面可以使用Timer注册事件回调,调用scheduleMicrotask函数注册微任务。这些回调不会立即执行,会在将来的某一时刻调用,这个过程称为异步,在循环中实现。
Future和Stream也是基于Timer和scheduleMicrotask实现异步的。
HandleMessage
在 dart sdk 中经常出现,跟事件循环相关。
如下:
dart sdk: sdk/lib/_internal/vm/lib/isolate_path.dart#_handleMessage
static _handleMessage(int id, var message) {
final handler = _portMap[id]?['handler'];
if (handler == null) {
return null;
}
// TODO(floitsch): this relies on the fact that any exception aborts the
// VM. Once we have non-fatal global exceptions we need to catch errors
// so that we can run the immediate callbacks.
handler(message);
_runPendingImmediateCallback();
return handler;
}
从 sdk 源码中可以清晰的了解底层的实现。在通常情况下是看不到实现细节的,从上面的路径可以看出,内部实现在_internal文件夹下。
dart VM在处理消息的最后会调用_handleMessage,这个过程就是一次循环,
阅读整个文件的代码,会发现handler是RawReceivePort注册的回调函数,而ReceivePort基于RawReceivePort实现,handler就是消息处理的入口。
接下来再看一下Timer的源码。
Timer
dart sdk: sdk/lib/_internal/vm/lib/timer_impl.dart#_createTimerHandler
static void _createTimerHandler() {
if (_receivePort == null) {
assert(_receivePort == null);
assert(_sendPort == null);
_receivePort = RawReceivePort(_handleMessage, 'Timer');
_sendPort = _receivePort!.sendPort;
_scheduledWakeupTime = 0;
} else {
(_receivePort as _RawReceivePortImpl)._setActive(true);
}
_receivePortActive = true;
}
知道如何使用Isolate吗,如:
final receivePort = ReceivePort();
final isolate = await Isolate.spawn(entryPoint,[receivePort.sendPort,...]);
receivePort.listen(_handleMessage);
//...
void _handleMessage(message) {
//...
}
void entryPoint(args) {
// new Isolate
}
在学习使用Isolate时,就要掌握ReceivePort的用法。
从这里就可以看出Timer也是使用RawReceivePort将_handleMessage设置为回调入口,也就是上面的handler。
当使用Timer时,会自动调用_createTimerHandler;注册的回调会在消息处理过程中调用。
循环机制的实现在C++端,flutter engine可以通过C++调用HandleMessage函数处理消息,最终调用dart_handleMessage。
Timer 与 scheduleMicrotask 的不同
从源码可以清晰看出它们之间的不同。
在一次事件循环过程中,会收集需要调用的Timer事件,收集完成后,再次注册的回调在下一次的循环中生效;scheduleMicrotask的实现是一个链表结构,在一个循环中通过判断是否到达尾端跳出循环,新注册的微任务会立即添加到末尾,这也就是微任务有可能造成死循环的原因,如下:
void main() {
_reg();
}
void _reg() {
scheduleMicrotask(_reg);
}
flutter engine 有所不同
在上面讲到事件循环过程中会收集Timer事件,在每一个事件调用之后,都会调用_runPendingImmediateCallback一次,这个是进入微任务循环的函数,最终调用_microtaskLoop进入循环。
在flutter engine: flutter/lib/ui/dart_runtime_hooks.cc#InitDartAsync中有进行一些变更,主要是scheduleMicrotask的hook;会发现_pendingImmediateCallback == null。
总结
dart中的异步最终都跟Timer或scheduleMicrotask有关,使用RawReceivePort监听并调用回调事件,完成异步。
很多方面没有涉及到,比如flutter engine 的 TaskRunner和dart中C++的api。
有错误之处欢迎指正。