dart:async
异步编程通常使用回调函数,但 Dart 提供了替代方案:Future 和 Stream 对象。Future 就像一个承诺,表示将来会提供一个结果。Stream 是一种获取一系列值(例如事件)的方式。Future、Stream 以及更多内容都在 dart:async 库中(API 参考)。
dart:async 库在 Web 应用和命令行应用中都有效。要使用它,请导入 dart:async
import 'dart:async';
Future
#Future 对象在整个 Dart 库中都很常见,通常作为异步方法返回的对象。当 future 完成时,它的值就可以使用了。
使用 await
#在您直接使用 Future API 之前,请考虑使用 await
代替。使用 await
表达式的代码可能比使用 Future API 的代码更容易理解。
考虑以下函数。它使用 Future 的 then()
方法按顺序执行三个异步函数,等待每个函数完成之后再执行下一个。
void runUsingFuture() {
// ...
findEntryPoint()
.then((entryPoint) {
return runExecutable(entryPoint, args);
})
.then(flushThenExit);
}
使用 await 表达式的等效代码看起来更像同步代码
Future<void> runUsingAsyncAwait() async {
// ...
var entryPoint = await findEntryPoint();
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
}
async 函数可以捕获来自 Futures 的异常。例如
var entryPoint = await findEntryPoint();
try {
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
} catch (e) {
// Handle the error...
}
有关使用 await
和相关 Dart 语言功能的更多信息,请参阅异步编程教程。
基本用法
#您可以使用 then()
来安排在 future 完成时运行的代码。例如,Client.read()
返回一个 Future,因为 HTTP 请求可能需要一段时间。使用 then()
可以让您在该 Future 完成并且承诺的字符串值可用时运行一些代码
httpClient.read(url).then((String result) {
print(result);
});
使用 catchError()
来处理 Future 对象可能抛出的任何错误或异常。
httpClient
.read(url)
.then((String result) {
print(result);
})
.catchError((e) {
// Handle or ignore the error.
});
then().catchError()
模式是 try
-catch
的异步版本。
链式调用多个异步方法
#then()
方法返回一个 Future,这提供了一种以特定顺序运行多个异步函数的有用方法。如果使用 then()
注册的回调返回一个 Future,则 then()
返回一个 Future,该 Future 将以与回调返回的 Future 相同的结果完成。如果回调返回任何其他类型的值,则 then()
会创建一个新的 Future,该 Future 以该值完成。
Future result = costlyQuery(url);
result
.then((value) => expensiveWork(value))
.then((_) => lengthyComputation())
.then((_) => print('Done!'))
.catchError((exception) {
/* Handle exception... */
});
在前面的示例中,方法按以下顺序运行
costlyQuery()
expensiveWork()
lengthyComputation()
这是使用 await 编写的相同代码
try {
final value = await costlyQuery(url);
await expensiveWork(value);
await lengthyComputation();
print('Done!');
} catch (e) {
/* Handle exception... */
}
等待多个 futures
#有时您的算法需要调用多个异步函数,并等待它们全部完成后才能继续。使用 Future.wait() 静态方法来管理多个 Futures 并等待它们完成
Future<void> deleteLotsOfFiles() async => ...
Future<void> copyLotsOfFiles() async => ...
Future<void> checksumLotsOfOtherFiles() async => ...
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');
Future.wait()
返回一个 future,该 future 在所有提供的 futures 完成后完成。它要么以它们的结果完成,要么在任何提供的 future 失败时以错误完成。
处理多个 futures 的错误
#您也可以等待 futures 的可迭代对象或记录上的并行操作。
这些扩展返回一个 future,其中包含所有提供的 futures 的结果值。与 Future.wait
不同,它们还允许您处理错误。
如果集合中的任何 future 以错误完成,wait
将以 ParallelWaitError
完成。这允许调用者处理各个错误并在必要时处置成功的结果。
当您不需要来自每个 future 的结果值时,请在 futures 的可迭代对象上使用 wait
void main() async {
Future<void> delete() async => ...
Future<void> copy() async => ...
Future<void> errorResult() async => ...
try {
// Wait for each future in a list, returns a list of futures:
var results = await [delete(), copy(), errorResult()].wait;
} on ParallelWaitError<List<bool?>, List<AsyncError?>> catch (e) {
print(e.values[0]); // Prints successful future
print(e.values[1]); // Prints successful future
print(e.values[2]); // Prints null when the result is an error
print(e.errors[0]); // Prints null when the result is successful
print(e.errors[1]); // Prints null when the result is successful
print(e.errors[2]); // Prints error
}
}
当您确实需要来自每个 future 的各个结果值时,请在 futures 的记录上使用 wait
。这提供了额外的优势,即 futures 可以是不同的类型
void main() async {
Future<int> delete() async => ...
Future<String> copy() async => ...
Future<bool> errorResult() async => ...
try {
// Wait for each future in a record, returns a record of futures:
(int, String, bool) result = await (delete(), copy(), errorResult()).wait;
} on ParallelWaitError<(int?, String?, bool?),
(AsyncError?, AsyncError?, AsyncError?)> catch (e) {
// ...
}
// Do something with the results:
var deleteInt = result.$1;
var copyString = result.$2;
var errorBool = result.$3;
}
Stream
#Stream 对象在整个 Dart API 中都很常见,表示数据序列。例如,HTML 事件(如按钮点击)是使用 streams 传递的。您还可以将文件作为 stream 读取。
使用异步 for 循环
#有时您可以使用异步 for 循环 (await for
) 而不是使用 Stream API。
考虑以下函数。它使用 Stream 的 listen()
方法订阅文件列表,传入一个函数字面量来搜索每个文件或目录。
void main(List<String> arguments) {
// ...
FileSystemEntity.isDirectory(searchPath).then((isDir) {
if (isDir) {
final startingDir = Directory(searchPath);
startingDir.list().listen((entity) {
if (entity is File) {
searchFile(entity, searchTerms);
}
});
} else {
searchFile(File(searchPath), searchTerms);
}
});
}
使用 await 表达式(包括异步 for 循环 (await for
))的等效代码看起来更像同步代码
void main(List<String> arguments) async {
// ...
if (await FileSystemEntity.isDirectory(searchPath)) {
final startingDir = Directory(searchPath);
await for (final entity in startingDir.list()) {
if (entity is File) {
searchFile(entity, searchTerms);
}
}
} else {
searchFile(File(searchPath), searchTerms);
}
}
有关使用 await
和相关 Dart 语言功能的更多信息,请参阅异步编程教程。
监听 stream 数据
#要获取到达的每个值,请使用 await for
或使用 listen()
方法订阅 stream
// Add an event handler to a button.
submitButton.onClick.listen((e) {
// When the button is clicked, it runs this code.
submitData();
});
在此示例中,onClick
属性是由提交按钮提供的 Stream
对象。
如果您只关心一个事件,可以使用 first
、last
或 single
等属性来获取它。要在处理事件之前对其进行测试,请使用 firstWhere()
、lastWhere()
或 singleWhere()
等方法。
如果您关心事件的子集,可以使用 skip()
、skipWhile()
、take()
、takeWhile()
和 where()
等方法。
转换 stream 数据
#通常,您需要在使用 stream 的数据之前更改其格式。使用 transform()
方法生成具有不同数据类型的 stream
var lines = inputStream
.transform(utf8.decoder)
.transform(const LineSplitter());
此示例使用了两个转换器。首先,它使用 utf8.decoder
将整数 stream 转换为字符串 stream。然后,它使用 LineSplitter
将字符串 stream 转换为单独行的 stream。这些转换器来自 dart:convert 库(请参阅dart:convert 部分)。
处理错误和完成
#您如何指定错误和完成处理代码取决于您是使用异步 for 循环 (await for
) 还是 Stream API。
如果您使用异步 for 循环,请使用 try-catch 来处理错误。在 stream 关闭后执行的代码放在异步 for 循环之后。
Future<void> readFileAwaitFor() async {
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
var lines = inputStream
.transform(utf8.decoder)
.transform(const LineSplitter());
try {
await for (final line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
}
如果您使用 Stream API,请通过注册 onError
监听器来处理错误。通过注册 onDone
监听器来运行 stream 关闭后的代码。
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
inputStream
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(
(String line) {
print('Got ${line.length} characters from stream');
},
onDone: () {
print('file is now closed');
},
onError: (e) {
print(e);
},
);
更多信息
#有关在命令行应用中使用 Future 和 Stream 的一些示例,请查看 dart:io 文档。另请参阅以下文章和教程
除非另有说明,否则本网站上的文档反映的是 Dart 3.7.1 版本。页面上次更新于 2025-02-12。 查看源代码 或 报告问题。