▲Dart-‘dart:async’库
Posted itzyjr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了▲Dart-‘dart:async’库相关的知识,希望对你有一定的参考价值。
dart:async
- 异步编程
异步编程通常使用回调方法来实现,但是 Dart 提供了其他方案:
Future
和
Stream
对象。 Future 类似与 javascript 中的 Promise,代表在将来某个时刻会返回一个结果。 Stream 类可以用来获取一系列的值,比如,一系列事件。
备忘:
你并不总是需要直接使用 Future 或 Stream 的 API。 Dart 语言支持使用关键字(例如,async 和 await )来实现异步编程。
dart:async 库可以工作在 web 应用及 command-line 应用。
从 Dart 2.1 开始,使用 Future 和 Stream 不需要导入 dart:async ,因为 dart:core 库 export 了这些类。
1. Future
在 Dart 库中随处可见 Future
对象,通常异步函数返回的对象就是一个 Future。当一个 future 完成执行后,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 函数返回 Future 对象。如果你不希望你的函数返回一个 future 对象,可以使用其他方案。例如,你可以在你的方法中调用一个 async 方法。
- 基本用法:
当 future 执行完成后,then() 中的代码会被执行。 then() 中的代码会在 future 完成后被执行。例如,HttpRequest.getString() 返回一个 future 对象,因为 HTTP 请求可能需要一段时间。当 Future 完成并且保证字符串值有效后,使用 then() 来执行你需要的代码,使用catchError()
来处理一些 Future 对象可能抛出的错误或者异常:
HttpRequest.getString(url).then((String result)
print(result);
).catchError((e)
// Handle or ignore the error.
);
then().catchError() 组合是 try-catch 的异步版本。
重要说明:
确保调用 catchError() 方式在 then() 的结果上,而不是在原来的 Future 对象上调用。否则的话,catchError() 就只能处理原来 Future 对象抛出的异常,而无法处理 then() 代码里面的异常。
- 链式异步编程:
then() 方法返回一个 Future 对象,这样就提供了一个非常好的方式让多个异步方法按顺序依次执行。如果用 then() 注册的回调返回一个 Future ,那么 then() 返回一个等价的 Future 。如果回调返回任何其他类型的值,那么 then() 会创建一个以该值完成的新 Future 。
下面示例中,方法按下面顺序执行:
1.costlyQuery() 2.expensiveWork() 3.lengthyComputation()
Future result = costlyQuery(url);
result.then((value) => expensiveWork(value))
.then((_) => lengthyComputation())
.then((_) => print('Done!'))
.catchError((exception)
/* Handle exception... */
);
使用 await 编写的等效代码:
try
final value = await costlyQuery(url);
await expensiveWork(value);
await lengthyComputation();
print('Done!');
catch (e)
/* Handle exception... */
- 等待多个Future:
有时代码逻辑需要调用多个异步函数,并等待它们全部完成后再继续执行。使用Future.wait()
静态方法管理多个 Future 以及等待它们完成:
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!');
2. Stream
在 Dart API 中 Stream
对象随处可见,Stream 用来表示一系列数据。例如,html 中的按钮点击就是通过 stream 传递的。同样也可以将文件作为数据流来读取。
- 异步循环:
有时,可以使用异步 for 循环await for
,来替代 Stream API 。
Stream的listen()
注册回调函数,该函数在每次在有更多数据可用、发生错误或流完成时执行。进一步的功能在stream上提供,通过调用stream来实现。
Stream<List<int>> stream = File('quotes.txt').openRead();
stream.transform(utf8.decoder).forEach(print);
下面示例函数,使用 Stream 的 listen()
方法来订阅文件列表,传入一个搜索文件或目录的函数,通过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) 实现的等价的代码,看起来更像是同步代码:
Future<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 for 前,确认这样能保持代码清晰,并希望获取所有 stream 的结果。例如,你通常并不会使用 await for 来监听 DOM 事件,因为 DOM 会发送无尽的流事件。如果在同一行使用 await for 注册两个 DOM 事件,那么第二个事件永远不会被处理。
- 监听流数据(stream data):
使用 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();
);
下面示例中,ID 为 “submitInfo” button 提供的 onClick 属性是一个 Stream 对象。
querySelector('#submitInfo')!.onClick.forEach((_) => print('Click.'));
如果只关心其中一个事件,可以使用,例如,first
, last
,或 single
属性来获取。要在处理时间前对事件进行测试,可以使用,例如 firstWhere()
, lastWhere()
,或 singleWhere()
方法。
如果只关心事件中的一个子集,可以使用,例如,skip()
, skipWhile()
,take()
,takeWhile()
,和 where()
。
Iterable<int> iterable = [1,2,3];
iterable.first;
iterable.last;
Iterable<int> iterable = -1,0,1,2;
//找到返回,找不到返回orElse的值。orElse是可选参数。
int negtive = iterable.firstWhere((item)=> item < 0,orElse: ()=>99);
print(negtive);// -1
- 传递流数据(stream data):
常常,在使用流数据前需要改变数据的格式。使用transform()
方法生成具有不同类型数据的流:
var lines = inputStream
.transform(utf8.decoder)
.transform(const LineSplitter());
上面例子中使用了两个 transformer 。第一个使用 utf8.decoder 将整型流转换为字符串流。接着,使用了 LineSplitter 将字符串流转换为多行字符串流。这些 transformer 来自 dart:convert
库。
- 处理错误和完成:
处理错误和完成代码方式,取决于使用的是异步 for 循环(await for)还是 Stream API 。
a. 如果使用的是异步 for 循环,那么通过 try-catch 来处理错误。代码位于异步 for 循环之后,会在 stream 被关闭后执行。
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);
b. 如果使用的是 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);
);
参考API:Stream
3. 更多内容
dart:io概览
Asynchronous programming: futures, async, await
Futures and error handling
Asynchronous programming: streams
Creating streams in Dart
Dart asynchronous programming: Isolates and event loops
以上是关于▲Dart-‘dart:async’库的主要内容,如果未能解决你的问题,请参考以下文章