用“工厂传送带”的方式理解 Dart/Flutter 事件循环

在 Flutter 中,理解任务(尤其是异步任务)是如何执行的,对于构建响应迅速的应用程序至关重要。本文中,我们将使用一个易于理解的"工厂传送带"比喻来深入解析 Dart 的事件循环。这个类比旨在为初学者简化概念,同时将其与实际的代码示例联系起来。


⚙️ 场景设定:单人工厂模型

想象你管理着一家具备以下配置的工厂:

  • 一名负责处理任务的工人
  • 一条将任务传送给工人的传送带

这名工人一次只能处理一项任务。所有任务会被分配到三个区域之一:

  1. 即时处理区:用于处理紧急且必须立即执行的任务(同步任务)
  2. 高优先级队列:用于处理体积小但重要的任务(微任务队列)
  3. 低优先级队列:用于处理其他所有任务(事件队列)

工人(Dart运行时)会按照这个优先级顺序挑选任务并进行相应处理。下面我们来详细了解每个区域。

🔧 任务管理的三大区域

1. 即时处理区(同步任务)

  • 描述: 这是处理那些必须"立即完成"的任务的区域。
  • 行为: 工人不能停止或切换到另一个任务,直到当前任务完成。
  • 在 Dart 中: 同步代码会立即运行并阻塞主线程。

示例:

dart 复制代码
void main() {
  print('Task 1 - Start');
  print('Task 1 - End');
}

输出:

arduino 复制代码
Task 1 - Start
Task 1 - End

在这里,即时处理区的所有任务都是按顺序完成的。

2. 高优先级队列(微任务队列)

  • 描述: 包含那些必须尽快运行,但可以等待同步任务完成的任务。
  • 行为: 工人会处理完所有微任务,然后才会处理优先级较低的任务。
  • 在 Dart 中: 微任务通常通过 scheduleMicrotask()Future 完成来调度。

示例:

dart 复制代码
import 'dart:async';

void main() {
  print('Task 1 - Start');

  scheduleMicrotask(() => print('Microtask 1'));
  Future(() => print('Event Queue Task 1'));

  print('Task 1 - End');
}

输出:

arduino 复制代码
Task 1 - Start
Task 1 - End
Microtask 1
Event Queue Task 1

微任务(如Microtask 1)在事件队列中的任务之前运行。

3. 低优先级队列(事件队列)

  • 描述: 包含用户输入、计时器或网络响应等任务。
  • 行为: 工人只有在所有同步任务和微任务队列中的任务都完成后,才会处理这些任务。
  • 在 Dart 中: 示例包括 I/O 回调、onTap 事件和 Timer 回调。

示例:

dart 复制代码
import 'dart:async';

void main() {
  print('Task 1 - Start');

  Timer(Duration(seconds: 1), () => print('Event Queue Task 1'));
  Future(() => print('Event Queue Task 2'));

  print('Task 1 - End');
}

输出:

arduino 复制代码
Task 1 - Start
Task 1 - End
Event Queue Task 2
Event Queue Task 1

事件队列中的任务会按顺序处理,但只有在更高优先级的任务完成后才会进行。


🚧 Async、Await 与"外部机器"

有时,某个任务会非常耗时 (例如,网络请求、文件 I/O、大量计算)。在我们的工厂比喻中,这些任务可以被移交给外部机器 🏭:

  • 工人(Dart 运行时)收到一个盒子 (一个 async 函数),这个盒子需要多个步骤才能完成。
  • 在处理过程中,工人意识到这一步需要一台机器(一个异步任务,例如调用 API 或读取文件)。
  • 工人给这一步贴上"await 机器"的标签,然后将盒子交给机器进行处理。
  • 此时,工人暂时**"搁置"这个盒子**(在 await 点暂停 async 函数),然后继续处理传送带上的其他盒子 (处理事件或微任务)。这意味着事件循环会继续运行其他任务,而不会被暂停的任务阻塞。
  • 一旦机器完成工作,盒子就会被送回 ,并附带一张纸条,上面写着"好了,完成了!"。Dart 会将 await 之后的代码 移入微任务队列 (或事件队列),供工人拾取并完成处理
  • 此时,工人(事件循环)恢复被暂停的盒子 (在 await 之后继续 async 函数),并将其运行至完成。

重要提示:

await不会阻塞整个应用程序。它只会暂停当前的异步函数。所有其他任务都会在事件循环上继续运行。

示例:

dart 复制代码
void main() async {
  print('Task 1 - Start');

  await Future.delayed(Duration(seconds: 2), () => print('Async Task Complete'));

  print('Task 1 - End');
}

输出:

arduino 复制代码
Task 1 - Start
Async Task Complete
Task 1 - End

它是这样运作的:

  • 工人开始"Task 1"。
  • await 处,工人将任务移交给一台外部机器(例如计时器)。
  • 在等待期间,工人处理其他任务(如果有的话)。
  • 当外部机器完成时,任务返回给工人,工人从上次中断的地方继续。

🏋️ 整合所有概念

事件循环的工作流程

  • 工人首先处理所有同步任务(即时处理区)。
  • 然后,它会处理微任务队列中的所有任务。
  • 最后,它会处理事件队列中的任务。
  • 如果在处理事件队列时有新任务出现在微任务队列中,工人会优先返回处理微任务队列。

这种优先级机制确保了应用程序的响应能力,并使你的 Flutter 应用保持流畅。

🎉 总结

通过"工厂传送带"的比喻来理解 Dart/Flutter 事件循环,有助于揭示同步和异步任务是如何管理的。通过正确使用 asyncawait 并理解任务优先级,你可以编写出高效且响应迅速的 Flutter 应用程序。

最后,欢迎关注我的微信公众号:OpenFlutter,感恩。

相关推荐
福兮说8 分钟前
纯前端把图片压缩到指定体积:canvas.toBlob 配合二分查找
前端·javascript·canvas·图片处理
是立不是利13 分钟前
CSS 入门与进阶:从选择器到响应式布局的完整指南
前端·css
镭立智能制造19 分钟前
电线电缆换线频繁?MES系统如何缩短换线时间40%
前端·制造
Amos_Web1 小时前
Rspack 源码解析(五):CodeGenerationPass 与模块代码生成
前端·前端框架·源码阅读
八荒启·交互动画1 小时前
Web特效06——WebGL深挖:顶点着色器和片元着色器到底在干什么
前端·网页特效·八荒启-交互动画·八荒启
这锅我不背哈1 小时前
前端权限控制实践:基于 RBAC 的完整落地方案
前端
星空1 小时前
Map<String, String>`Map`是接口,不能直接 new
java·前端·算法
开开心心就好2 小时前
低年级识字练笔顺工具,电脑和安卓端都能用
前端·javascript·网络·安全·scala·erlang·语音识别
杨利杰YJlio2 小时前
ITSK PE 26U5 测试版解读:组件完善、VMD 修复与服务器支持边界
前端·javascript·后端
志尊宝2 小时前
Vue3 零基础每日笔记(021):class 与 style 动态绑定——对象、数组、三元表达式写法汇总
前端·vue.js·笔记·前端框架·html5