在 dart:async 库中,要先 import。
Future
delayed 创建一个延时任务。
Future.delayed(new Duration(seconds: 2),(){
return "hi world!";
}).then((data){
print(data);
});
String lookUpVersion() => '1.0.0';
如果将其改变成一个异步函数,返回值将是一个 Future,异步函数要有 async 关键字:
Future lookUpVersion() async => '1.0.0';
如果函数没有返回一个有用的值,那么返回 Future
类型。
设有三个异步函数
Future login(String userName, String pwd) async {
//用户登录
return '$userName-pwd'
}
Future getUserInfo(String id) async {
//获取用户信息
return 'user id $id'
}
// 保存用户信息
Future saveUserInfo(String userInfo) async => print(userInfo)
void main() {
login("alice","1234").then((id) {
// 登录成功后通过,id 指代返回的值
getUserInfo(id).then((userInfo) {
// 获取用户信息后保存,无返回值时参数表示异常
saveUserInfo(userInfo).then((e){
// 保存用户信息,接下来执行其它操作
});
});
});
}
结果输出 user id alice-pwd
。
链接多个异步函数
如果有大量的异步函数,像上面那样就会产生回调地狱。首先可以通过链式调用避免
void main() {
login("alice","1234").then((id) {
return getUserInfo(id);
}).then((userInfo) => saveUserInfo(userInfo)
).then((e){
}).catchError((e) {
// 使用 catchError() 来处理 Future 对象可能抛出的任何错误或异常
});
}
then().catchError()
模式是 try-catch 的异步版本。
一定要在 then() 的结果上调用 catchError(),而不是在原始 Future 的结果上。否则,catchError() 只能从原始 Future 的计算中处理错误,而不能从 then() 注册的处理程序处理错误。
使用 await
还可以用 await 改造,使异步代码看起来像同步一样。
await 必须在 async 标注的异步函数中调用,返回一个 Future 对象。await 表达式会让程序执行挂起,直到返回的对象可用。
void main() async {
try { // 可以捕获异常
var id = await login("alice","1234");
var userInfo = await getUserInfo(id);
await saveUserInfo(userInfo);
} catch (e) {
}
}
等待多个 Future
有时需要调用许多异步函数,并等待它们全部完成后再继续。使用 Future.wait()
静态方法管理多个 Future,并等待它们完成:
Future deleteLotsOfFiles() async => ...
Future copyLotsOfFiles() async => ...
Future checksumLotsOfOtherFiles() async => ...
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');
onError, whenComplete
then 有一个可选参数 onError,可以处理异常。
Future.delayed(new Duration(seconds: 2), () {
//return "hi world!";
throw AssertionError("Error");
}).then((data) {
print("success");
}, onError: (e) {
print(e);
});
无论异步任务执行成功或失败都要做的事,在 Future 的 whenComplete 回调里执行
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}).then((data){
// 执行成功会走到这里
print(data);
}).catchError((e){
// 执行失败会走到这里
print(e);
}).whenComplete((){
// 无论成功或失败都会走到这里
});
Stream
也是用于接收异步事件数据,和 Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。
Stream.fromFutures([
// 1秒后返回结果
Future.delayed(new Duration(seconds: 1), () {
return "hello 1";
}),
// 抛出一个异常
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒后返回结果
Future.delayed(new Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
});
结果
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
使用异步 for 循环
有时可以使用异步 for 循环(wait for),而不是使用流 API。
下面的函数使用 Stream 的 listen() 方法订阅一个文件列表,传入一个搜索每个文件或目录的函数文字。
void main(List arguments) {
// ...
FileSystemEntity.isDirectory(searchPath).then((isDir) {
if (isDir) {
final startingDir = Directory(searchPath);
startingDir
.list(
recursive: argResults[recursive],
followLinks: argResults[followLinks])
.listen((entity) {
if (entity is File) {
searchFile(entity, searchTerms);
}
});
} else {
searchFile(File(searchPath), searchTerms);
}
});
}
带有 await 表达式的等价代码,包括异步 for 循环(await for),看起来更像同步代码:
Future main(List arguments) async {
// ...
if (await FileSystemEntity.isDirectory(searchPath)) {
final startingDir = Directory(searchPath);
await for (var entity in startingDir.list(
recursive: argResults[recursive],
followLinks: argResults[followLinks])) {
if (entity is File) {
searchFile(entity, searchTerms);
}
}
} else {
searchFile(File(searchPath), searchTerms);
}
}
监听流数据
要在每个值到达时获得它,可以使用 await() 方法对流使用或使用 listen() 方法订阅:
// Find a button by ID and add an event handler.
querySelector('#submitInfo').onClick.listen((e) {
// When the button is clicked, it runs this code.
submitData();
});
onClick 属性是 “submitInfo” 按钮提供的流对象。
如果只关心一个事件,那么可以使用属性 first、last 或 single 来获得它。要在处理事件之前测试它,可以使用诸如 firstWhere()、lastWhere() 或 singleWhere() 之类的方法。
如果关心事件的子集,可以使用诸如 skip()、skipWhile()、take()、takeWhile() 和 where() 等方法。
改变流数据
通常需要在使用流数据之前更改其格式。使用 transform() 方法生成具有不同类型数据的流:
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
首先使用 utf8.decoder 将整数流转换为字符串流。然后使用 LineSplitter 将字符串流转换为单独的行流。这些转换器来自 dart:convert 库。
处理错误和完成
如何指定错误和完成处理代码取决于是使用异步 for 循环(wait for)还是流 API。
如果使用异步 for 循环,则使用 try-catch 处理错误。在流关闭后执行的代码在异步 for 循环之后执行。
Future readFileAwaitFor() async {
var config = File('config.txt');
Stream> inputStream = config.openRead();
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
try {
await for (var line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
}
如果使用流 API,则通过注册 onError 侦听器来处理错误。通过注册 onDone 侦听器,在流关闭后运行代码。
var config = File('config.txt');
Stream> inputStream = config.openRead();
inputStream
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((String line) {
print('Got ${line.length} characters from stream');
}, onDone: () {
print('file is now closed');
}, onError: (e) {
print(e);
});
可以使用 break 或 return 语句,该语句将跳出 for 循环,并从流中取消订阅。