dart 事件循环与异步

322 阅读2分钟

首先我们要知道事件循环和异步之间的关系。在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,这个过程就是一次循环, 阅读整个文件的代码,会发现handlerRawReceivePort注册的回调函数,而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中的异步最终都跟TimerscheduleMicrotask有关,使用RawReceivePort监听并调用回调事件,完成异步。
很多方面没有涉及到,比如flutter engine 的 TaskRunner和dart中C++的api。
有错误之处欢迎指正。