用“工厂传送带”的方式理解 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,感恩。

相关推荐
fuyongliang1233 小时前
Linux shell 脚本基础 003
java·服务器·前端
lypzcgf6 小时前
Coze源码分析-工作空间-项目开发-前端源码
前端·人工智能·typescript·系统架构·开源软件·react·安全架构
yuguo.im6 小时前
Chrome DevTools Performance 是优化前端性能的瑞士军刀
前端·javascript·性能优化·chrome devtools
FSHOW7 小时前
【独立开发日记】MQ端到端类型安全
前端·javascript·后端
老华带你飞8 小时前
社区互助|基于SSM+vue的社区互助平台的设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·小程序·毕设·社区互助平台
一支鱼8 小时前
前端使用次数最多的工具封装
前端·typescript·编程语言
GIS之路8 小时前
GDAL 简介
前端
前端工作日常8 小时前
单元测试与E2E测试中使用浏览器的原因及区别
前端·单元测试
Js_cold9 小时前
Notepad++使用技巧1
前端·javascript·fpga开发·notepad++
接着奏乐接着舞。9 小时前
前端RSA加密遇到Java后端解密失败的问题解决
java·开发语言·前端