Dart 中的事件循环和微任务(scheduleMicrotask)

Dart 应用程序有一个带有两个队列的事件循环 ------ 事件 队列微任务队列

Dart 程序的执行流程始于主 isolatemain 函数,然后可以通过隔离体机制创建和管理其他隔离体。每个子隔离体都可以有自己的入口点函数, 也就是耗时任务的函数。

事件循环和队列

在客户端应用中,主 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);
}

处理入口函数的延迟调用的。

  1. 创建一个 RawReceivePort 对象,这是 Dart 中用于在隔离体之间进行消息通信的一种机制。

  2. 为这个 RawReceivePort 对象设置一个事件处理器(handler),当接收到消息时,这个处理器会被触发。

  3. 在事件处理器中,首先关闭了这个 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 所示的函数。 tag2entryPoint() 方法就是 main 方法。

参考资料

Dart 中的并发

相关推荐
brzhang10 分钟前
效率神器!TmuxAI:一款无痕融入终端的AI助手,让我的开发体验翻倍提升
前端·后端·算法
海底火旺12 分钟前
JavaScript 原型链检查:从 `instanceof` 到 `isPrototypeOf` 的演进
前端·javascript·面试
埃兰德欧神12 分钟前
Lynx:革新跨端开发,一次编写,多端闪耀
前端·javascript·前端框架
贾公子15 分钟前
详解 LeetCode 第 242 题 - 有效的字母组
前端
前端太佬21 分钟前
小程序登录与授权功能全解析:从原理到设计的实战指南 (2025年最新规范实践版)
前端·微信·微信小程序
一抓掉一大把26 分钟前
elementui日历显示红点及根据日程范围判断是否有红点
前端·javascript·elementui
前端.火鸡26 分钟前
使用wavesurferJs实现录音音波效果
开发语言·前端·javascript
anyup34 分钟前
借助 Trae 从 0 到 1 实现海外地图渲染(leaflet + OSM)
前端·数据可视化·trae
六边形66638 分钟前
JavaScript 中本地对象、内置对象、宿主对象的区别与作用
前端·javascript
snakeshe101042 分钟前
深入解析React Hooks:useState与useReducer的区别与实现
前端