FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • Flutter - Dart特性语法
  • Flutter - Dart事件循环机制与异步
    • 一、Dart 异步
      • 1、同步与异步
      • 2、单线程模型
      • 3、事件循环机制
    • 二、Future
      • 1、基本使用
      • 2、链式调用
      • 3、其它 API
    • 三、async/await
      • 1、基本使用
    • 四、isolate
      • 1、创建 isolate (Dart API)
      • 2、isolate 通信 (单向)
      • 3、创建 isolate (Flutter API)
    • 参考资料
  • Flutter - StateWidget与生命周期
  • Flutter - 基础Widget
  • Flutter - 基础布局
  • Flutter - ListView与GridView
  • 《Flutter从入门到实战》
GitLqr
2021-04-07
目录

Flutter - Dart事件循环机制与异步

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、Dart 异步

与 JavaScript 一样, Dart 是基于 事件循环机制 的 单线程模型, 所以 Dart 中没有多线程, 也就没有主线程与子线程之分.

# 1、同步与异步

  • 同步: 同一线程中, 按照代码的编写顺序, 自上而下依次执行 (直观感受: 需要等待)
  • 异步: 代码执行中, 某段代码的执行不会影响后面代码的执行 (直观感受: 无需等待)

# 2、单线程模型

# 单线程模型:

  • 一条执行线上, 同时且只能执行一个任务(事件), 其他任务都必须在后面排队等待被执行.
  • 为了不阻碍代码的执行, 每遇到的耗时任务都会被挂起放入任务队列, 待执行结束后再按放入顺序依次执行队列上的任务, 从而达到异步效果.

# 单线程模型 与 多线程 各自的优势:

  • 单线程模型的优势: 避免了多线程的缺点, 比较适合于需要等待对方传送数据或返回结果的耗时操作, 如网络请求, IO 操作等.
  • 多线程的优势: 尽可能利用处理器的多核实现并行计算的计算密集型操作.

# 多线程 的缺点:

  • 会带来额外的资源和性能消耗.
  • 多个线程操作共享内存时需要加锁控制, 锁竞争会降低性能和效率, 复杂情况下还容易造成死锁.

# 3、事件循环机制

对于用户点击, 滑动, 硬盘 IO 访问等事件, 你不知道何时发生或以什么顺序发生, 所以得有一个永不停歇且不能阻塞的循环来等待处理这些 "突发" 事件. 于是, 基于 事件循环机制 的 单线程模型 就出现了:

Dart 事件循环机制由 一个消息循环(Event Looper) 和 两个消息队列(Event Queue) 构成, 这两个消息队列分别是: 事件队列(Event queue) 和 微任务队列(MicroTask queue).

# Event Looper

Dart 在执行完 main 函数后, Event Looper 就开始工作, Event Looper 优先全部执行完 Microtask Queue 中的 event, 直到 Microtask Queue 为空时, 才会执行 Event Looper 中的 event, Event Looper 为空时才可以退出循环.

注意: Event Looper 为空时, 是 可以 而不是 一定 要退出, 视场景而定.

# Event Queue

Event Queue 的 event 来源于 外部事件 和 Future

  • 外部事件: 例如输入/输出, 手势, 绘制, 计时器, Stream 等
  • Future: 用于自定义 Event Queue 事件

对于外部事件, 一旦没有任何 microtask 要执行, Event loop才会考虑 event queue中的第一项,并且将会执行它.

通过 Future 实例向 Event Queue 添加事件:

Future(() {
  // 事件任务
});

# Microtask Queue

  • Microtask Queue 的优先级高于 Event Queue.
  • 使用场景: 想要在稍后完成一些任务(microtask) 但又希望在执行下一个事件(event)之前执行.

Microtask 一般用于非常短的内部异步动作, 并且任务量非常少, 如果微任务非常多, 就会造成 Event Queue 排不上队, 会阻塞 Event Queue 的执行(如: 用户点击没有反应). 所以, 大多数情况下优先考虑使用 Event Queue, 整个 Flutter 源代码仅引用 scheduleMicroTask() 方法 7 次.

通过 scheduleMicroTask() 函数向 Microtask Queue 添加任务:

scheduleMicrotask(() {
  // 微任务
});

# 二、Future

Dart 中的异步操作主要使用 Future 与 async/await, 整体与前端 ES6 中的 Promise, async/await 的使用差不多, 可以把 Future 理解为是一个自带 callback 效果的类.

# 1、基本使用

通过查看 Future 的构造函数知道, 创建时需要传入一个返回值类型是 FutureOr<T> 的函数:

factory Future(FutureOr<T> computation()) {
  ...
}

这个 FutureOr<T> 是一个联合类型, 最终类型可能是 Future 或是泛型 T 的具体类型. 当不指定泛型 T 时, 实例类型为 Future<dynamic>. 下面是一个模拟网络耗时请求的例子:

Future<String> getNetworkData() {
  // 1. 将耗时操作包裹到Future的回调函数中
  return Future<String>(() {
    sleep(Duration(seconds: 2));

    return "Hello lqr"; // 只要有返回结果, 那么就执行Future对应的then的回调(相当于Promise-resolve)
    // throw Exception("error"); // 如果没有结果返回(有错误信息), 需要在Future回调中抛出一个异常(相当于Promise-reject)
  });
}

Future 实例有 3 个常用方法:

  • then((value){...}): 正常运行时执行
  • catchError((err){...}): 出现错误时执行
  • whenComplete((){...}): 不管成功与否都会执行

通过以上 3 个方法, 即可获得 Future 实例的执行状况与结果:

main(List<String> args) {
  print("main start");

  // 2. 拿到结果
  var future = getNetworkData();
  future.then((value) => print(value)) // Hello lqr
      .catchError((err) => print(err))
      .whenComplete(() => print("执行完成")); // 不管成功与否都会执行

  print("main end");
}

日志输出如下:

main start
main end

// 2秒后输出:
Hello lqr
执行完成

注意, 以上 3 个方法是可以分开写的, 但每次执行完一个方法时需要对 future 实例重新赋值(相当于包了一层), 否则后续方法无效:

var future = getNetworkData();

// 错误写法:
future.then((value) => print(value));
future.catchError((error) => print(error)); // 无效

// 正确写法:
future = future.then((value) {
  print(value);
  return value;
});
future.catchError((error) => print(error)); // 有效

# 2、链式调用

Future 可以在 then()方法中返回另一个 Future 实例, 从而达到链式调用的效果, 这对那些有数据关联的网络请求很有用:

main(List<String> args) {
  print("main start");

  // 链式调用, 执行多个数据处理
  Future(() {
    return "第一次结果";
  }).then((value) {
    print(value);
    return "第二次结果";
  }).then((value) {
    print(value);
    return "第三次结果";
  }).then((value) {
    print(value);
  }).catchError((error) {
    print(error);
  });

  print("main end");
}

强调: Future 构造函数要求传入一个返回值类型是 FutureOr<T> 的函数, 但因为 FutureOr<T> 是联合类型, 所以, 这里可以返回另一个 Future 实例, 或者是一个具体类型数据, 比如字符串.

# 3、其它 API

Future 除了默认构造器外, 还提供了几个常用的命名构造器:

  • Future.value(): 创建一个返回具体数据的 Future 实例
  • Future.error(): 创建一个返回错误的 Future 实例
  • Future.delayed(): 创建一个延时执行的 Future 实例
main(List<String> args) {
  print("main start");

  Future.value("Hello lqr").then((value) => print(value));

  Future.error("出错了").catchError((error) => print(error));

  Future.delayed(Duration(seconds: 3))
      .then((value) => "Hello lqr")
      .then((value) => print(value));

  Future.delayed(Duration(seconds: 2), () => "Hello lqr")
      .then((value) => print("welcome"))
      .then((value) => throw Exception("出错了"))
      .catchError((error) => print(error))
      .whenComplete(() => print("执行完成")); // 不管成功与否都会执行

  print("main end");
}

# 三、async/await

async/await 是 Dart 提供的可以用 同步的代码格式 实现 异步的调用过程 的 语法糖.

# 1、基本使用

  • await 必须在 async 函数中使用
  • async 函数返回的结果必须是一个 Future
Future getNetworkData() async {
  var userId = await getUserId();
  var userInfo = await getUserInfo(userId);
  return userInfo.username // 会自动包裹成Future
}

如果不使用 async/await, 那么上面的代码则需要这么写:

Future getNetworkData() {
  return getUserId().then((userId) {
    return getUserInfo(userId);
  }).then((userInfo) {
    return userInfo.username;
  });
}

相比之下, 使用 async/await 写出来的代码在理解上会更加清晰.

# 四、isolate

所有的 Dart 代码都是在 isolate 中运行的, 它就是机器上的一个小空间, 具有自己的私有内存块和一个运行着 Event Looper 的单个线程. 每个 isolate 都是相互隔离的, 并不像线程那样可以共享内存. 一般情况下, 一个 Dart 应用只会在一个 isolate 中运行所有代码, 但如果有特殊需要, 可以开启多个:

注意: Dart 中没有线程的概念, 只有 isolate .

# 1、创建 isolate (Dart API)

Dart 默认提供了 Isolate.spawn(entryPoint, message) 用于开启 isolate, 通过源码可以知道形参 message 其实是 形参 entryPoint 对应的函数执行时需要的参数:

external static Future<Isolate> spawn<T>(
    void entryPoint(T message), T message,
    {bool paused = false,
    bool errorsAreFatal = true,
    SendPort? onExit,
    SendPort? onError,
    @Since("2.3") String? debugName});

使用 Isolate.spawn(entryPoint, message) 开启 isolate, 并指定要执行的任务:

import 'dart:isolate';

main(List<String> args) {
  print("main start");

  Isolate.spawn(calc, 100);

  print("main end");
}

void calc(int count) {
  var total = 0;
  for (var i = 0; i < count; i++) {
    total += i;
  }
  print(total);
}

# 2、isolate 通信 (单向)

isolate 间可以一起工作的唯一方法是通过来回传递消息. 一般情况下, 子isolate 会将运行结果通过管道以消息的形式发送到 主isolate, 并在 主isolate 的 Event Looper 中处理该消息, 这时就需要借助 ReceivePort 来处理消息的传递了:

  • 在启动 子isolate 时, 将 主isolate 的发送管道(SendPort)作为参数传递给 子isolate.
  • 子isolate 在执行完毕时, 可以利用管道(SendPort)给 主isolate 发送信息.
import 'dart:isolate';

main(List<String> args) async {
  print("main start");

  // 1. 创建管道
  var receivePort = ReceivePort();

  // 2. 创建isolate
  Isolate isolate = await Isolate.spawn(foo, receivePort.sendPort);

  // 3. 监听管道
  receivePort.listen((message) {
    print(message);
    // 不再使用时, 关闭管道
    receivePort.close();
    // 不再使用时, 将 isolate 杀死
    isolate.kill();
  });

  print("main end");
}

void foo(SendPort sendPort) {
  sendPort.send("Hello lqr");
}

以上只实现了 isolate 的单向通信, 双向通信比较麻烦, 有兴趣可以再查看一些其他资料.

# 3、创建 isolate (Flutter API)

Flutter 提供了更为方便的开启 isolate 的 API: compute() 函数. 以下是示例代码:

main(List<String> args) async {
  int result = await compute(powerNum, 5);
  print(result);
}

int powerNum(int num) {
  return num * num;
}

compute() 是 Flutter 的 API, 不是 Dart 的 API, 所以, 上面的代码只能在 Flutter 项目中才能运行.

# 参考资料

  • Flutter 开发之 Dart 线程与异步 (opens new window)
  • Dart asynchronous programming: Isolates and event loops (opens new window)
  • Futures - Isolates - Event Loop (opens new window)
#事件循环机制#单线程模型#Future#async#await#isolate
Flutter - Dart特性语法
Flutter - StateWidget与生命周期

← Flutter - Dart特性语法 Flutter - StateWidget与生命周期→

最近更新
01
Flutter - Xcode16 还原编译速度
04-05
02
AI - 免费的 Cursor 平替方案
03-30
03
Android - 2025年安卓真的闭源了吗
03-28
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×