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

相关推荐
然我几秒前
闭包在类封装中的神技:实现真正安全的私有属性,面试必懂的封装技巧
前端·javascript·面试
小公主几秒前
别再用 props 一层层传了!React useContext 真正用法与 useState 深度解析
前端·react.js
帅夫帅夫2 分钟前
深入理解 JavaScript 的 const:从基础到内存原理
前端
用户名1233 分钟前
我写了个脚本,让前端彻底告别 Swagger 手动搬砖
前端
爱编程的喵5 分钟前
深入理解JavaScript节流函数:从原理到实战应用
前端·javascript·html
尧木晓晓5 分钟前
开发避坑指南:Whistle 代理失效背后,localhost和 127.0.0.1 的 “爱恨情仇” 与终极解决方案
前端·javascript
风无雨38 分钟前
GO启动一个视频下载接口 前端可以边下边放
前端·golang·音视频
aha-凯心1 小时前
前端学习 vben 之 axios interceptors
前端·学习
熊出没2 小时前
Vue前端导出页面为PDF文件
前端·vue.js·pdf
VOLUN2 小时前
Vue3项目中优雅封装API基础接口:getBaseApi设计解析
前端·vue.js·api