异步支持

优质
小牛编辑
127浏览
2023-12-01

Dart 的库充满了返回 FutureStream 对象的函数。这些函数是“异步的”:它们在设置一个可能比较耗时的操作(比如 I/O)后返回,而不去等待操作完成。

关键字 asyncawait 支持异步编程,可以使你用看起来像同步的方式编写异步代码。

处理 Futures

当你需要一个已完成的 Future 的结果时,你有两个选择:

  • 使用 asyncawait
  • 使用 Future API,如 Dart 库教程 中所述。

使用 asyncawait 的代码是异步的,但看起来像同步代码。比如,下面的代码使用 await 来等待一个异步函数的返回:

await lookUpVersion();

要使用 await,代码必须在一个”异步函数“中——一个标记为 async 的函数:

Future checkVersion() async {
  var version = await lookUpVersion();
  // 使用 version 做一些操作
}

说明:虽然异步函数可能会执行耗时的操作,但它不会等待这些操作。相反,异步函数只在遇到第一个 await 表达式时执行(详情)。然后它返回一个 Future 对象,仅在 await 表达式完成后才恢复执行。

在使用 await 的代码中,可以使用 trycatchfinally 来处理错误和清理代码。

try {
  version = await lookUpVersion();
} catch (e) {
  // 处理查找版本号失败的情况
}

你可以在一个异步函数中多次使用 await。比如,下面的代码等待了三次函数的结果:

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

await expression 中,expression 的值通常是一个 Future;如果它不是,这个值会自动封装成一个 Future。这个 Future 对象表示返回一个对象的承诺。而 await expression 的值就是这个返回的对象。Await 表达式会导致执行暂停直到这个对象可用。

如果你在使用 await 时遇到了编译期错误,请确保 await 在异步函数内。比如,要在你的应用的 main() 函数中使用 awaitmain() 的函数体必须标记为 async

Future main() async {
  checkVersion();
  print('In main: version is ${await lookUpVersion()}');
}

声明异步函数

“异步函数“就是函数体被 async 修饰符标记的函数。

添加 async 关键词到一个函数会使它返回一个 Future。比如,考虑使用同步函数,返回一个字符串:

String lookUpVersion() => '1.0.0';

如果你修改它为一个异步函数——比如,因为之后的一个实现会是耗时的——它的返回值是一个 Future:

Future<String> lookUpVersion() async => '1.0.0';

注意函数体并不一定要使用 Future API。Dart 会在必要的时候自动创建 Future。如果你的函数不返回有用的值,使它返回 Future<void>

有关使用 future、async 和 await 的互动介绍,请参阅异步编程实验室

处理 Streams

当你需要从一个 Stream 中获取值是,你有两个选择:

  • 使用 async 和一个”异步 for 循环“ (await for)
  • 使用 Stream API,如 Dart 库教程 中所述。

说明:在使用 await for 前,请确保代码足够清晰并且你真的想要等待 stream 中所有的结果。比如,你通常需要对 UI 事件监听器使用 await for,因为 UI 框架不停地发送事件的 stream。

一个异步 for 循环拥有下面的格式:

await for (varOrType identifier in expression) {
  // 每当 stream 发送一个值时执行一次
}

Expression 的值必须是 Stream 类型。按照如下步骤执行:

  1. 等待 stream 发送一个值。
  2. 执行 for 循环的循环体,使用发送的值作为变量值。
  3. 重复 1 和 2 直到 stream 被关闭。

要停止监听这个 stream,你可以使用 break 或者 return 语句,它们会打断 for 循环并且取消对 stream 的订阅。

如果你在使用异步 for 循环时遇到了编译期错误,请确保 await for 在一个异步函数内。比如,要在你的应用的 main() 函数中使用异步 for 循环,main() 的函数体必须标记为 async

Future main() async {
  // ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  // ...
}

要了解更多关于异步编程的信息,通常地,参阅 dart:async 部分的库文档。