Dart 的异步架构

Posted on Wed, 25 Dec 2024 11:06:06 +0800 by LiangMingJian


概述

Dart 是一种单线程编程语言,如果任何代码阻塞线程执行都会导致程序卡死。为了避免此类情况出现,Dart 使用 Future 对象表示异步操作。

// Synchronous code
printDailyNewsDigest() {
  String news = gatherNewsReports(); // Can take a while.
  print(news);
}

main() {
  printDailyNewsDigest();
  printWinningLotteryNumbers();
  printWeatherForecast();
  printBaseballScore();
}

在上述示例代码中,存在一个问题函数 printDailyNewsDigest,该函数是阻塞的,在这之后的代码都必须等待 printDailyNewsDigest 结束才能继续执行。因此为了程序能及时响应,Dart 的作者使用异步编程模型 Future 处理可能耗时的函数。

什么是 Future

Future 表示在将来某时获取一个值的方式。当一个返回 Future 的函数被调用的时候,程序做了两件事情:

  • 函数把自己放入队列和返回一个未完成的 Future 对象
  • 当值可用时,Future 带着值变成完成状态。

async 和 await

async 和 await 关键字是 Dart 异步支持的一部分。他们允许你像写同步代码一样写异步代码和不需要使用 Future 接口。

import 'dart:async';

printDailyNewsDigest() async {
  String news = await gatherNewsReports();
  print(news);
}

main() {
  printDailyNewsDigest();
  printWinningLotteryNumbers();
  printWeatherForecast();
  printBaseballScore();
}

如上述示例代码,在这时 printDailyNewsDigest 虽然是第一个调用的,但是最后打印的。这是因为代码读取和打印内容是异步执行的。

在这个例子中,printDailyNewsDigest 调用 gatherNewsReports 并不会阻塞程序。gatherNewsReports 会把自己放入队列,返回一个 Future 让程序正常执行下去,在 gatherNewsReports 完成收集新闻过后程序再来进行打印。

下面的图展示代码的执行流程。每一个数字对应着相应的步骤

  1. 开始程序执行
  2. main 函数调用 printDailyNewsDigest,因为它被标记为 async,所有在该函数任何代码被执行之前立即返回一个 Future
  3. 剩下的打印执行。因为它们是同步的。所有只有当一个打印函数执行完成过后才能执行下一个打印函数。例如:中奖号码在天气预报执行打印。
  4. 函数 printDailyNewsDigest 函数体开始执行
  5. 在到达 await 之后,调用 gatherNewsReports,程序暂停,等待 gatherNewsReports 返回的 Future 完成。
  6. 当 Future 完成,printDailyNewsDigest 继续执行,打印新闻。
  7. 当 printDailyNewsDigest 执行完成过后,最开始的 Future 返回完成,程序退出。
  8. PS:如果 async 函数没有明确指定返回值,返回的 null 值的 Future

从上面的示例可以看出,async 和 await 在使用时具有以下的规则。

  • await 必须在 async 方法中使用。
  • async 方法中在 await 前面的代码会立即同步执行,直到碰到 await。
  • await 的作用是等待所标记的方法获取返回结果。当代码跑到 await 时,程序其他部分立即停止,直到标记方法执行完成
  • 当代码执行到 await,程序会立即返回一个 future。

错误处理

如果在 Future 返回时发生错误,你可能想捕获错误。async 函数可以用 try-catch 捕获错误。

printDailyNewsDigest() async {
  try {
    String news = await gatherNewsReports();
    print(news);
  } catch (e) {
    // Handle error...
  }
}

连续执行

你可以使用多个 await 表达式,保证一个 await 执行完成过后再执行下一个

main() async {
  await expensiveA();
  await expensiveB();
  doSomethingWith(await expensiveC());
}
参考文件 1: Dart异步编程-future @方田