flutter中的异步(二)
什么是Future对象
Future是一种对异步任务的结果的封装,也可以理解为是对暂时未完成的任务的一种预期。
Future是可以指定泛型的,下面的例子就是Future返回了一个File类型的结果。
js
void saveToFile(TaskResult result) {
String filePath = path.join(Directory.current.path, "out.json");
File file = File(filePath);
String content = json.encode(result);
Future<File> futureFile = file.writeAsString(content);
}
Future对象在任务开始时就已经生成了,但是在诞生的时候Future对象并不知道最终会生成什么样的结果,因此Future对象需要监听任务回调的结果,dart中提供了两个工具来完成这一任务分别是then
和catchError
,这两个工具分别用来监听成功完成和异常结束的场景。
Future对任务回调的监听
js
void saveToFile(TaskResult result) {
String filePath = path.join(Directory.current.path, "out.json");
File file = File(filePath);
String content = json.encode(result);
Future<File> futureFile = file.writeAsString(content);
futureFile.then((File file) { // tag1
print('写入成功:${file.path}');
});
}
Future#then
方法用来监听文件写入的结果。
js
void saveToErrorFile(TaskResult result) {
String filePath = path.join(Directory.current.path, "error","out.json");
File file = File(filePath);
String content = json.encode(result);
Future<File> futureFile = file.writeAsString(content);
// 监听任务成功完成
futureFile.then((File file) {
print('写入成功:${file.path}');
});
// 监听任务异常结束
futureFile.catchError((err) {
print("catchError:$err");
});
}
catchError
方法用来监听任务是否写入异常,并对异常结果进行自定义操作。
有的时候我们既需要监听任务完成时的结果,又需要监听异常同时还要保证任务不会被终止,这时候可以使用whenComplete
方法(这个方法有点像java中的finally
,即不管代码是否出现异常都会完成接下去的操作)
FutureOr对象
FutureOr是一个比较特别的对象,它是一个私有构造的抽象类,并不可以被实例化,并且他代表Future和类型,这是一个一类两型的特殊存在(即File和Future都可以被看做是FutureOr对象)
catchError方法的返回值
js
futureFile = futureFile.catchError((err){
print("catchError::[${err.runtimeType}]::$err");
return File('this is error file');
});
futureFile.then((value){
print(value.path);
});
当发生异常时会打印"This is error file",当不发生异常时会正常输出
异步成功回调
then方法的返回值
异步成功回调一般使用then
去监听回调结果,then方法的定义如下:
js
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
第一个入参是函数对象,其中函数参数是 T
类中值,也就是 Future
的泛型类型;该函数还有一个返回值,类型为 FutureOr<R>
。其中 R
泛型是方法指定的泛型,then
方法的返回值是 R
泛型的 Future
对象。 以下为具体使用实例:
js
Future<String> thenResult = futureFile.then<String>((File file) {
print('写入成功:${file.path}');
return file.readAsString();
});
thenResult.then((String value){
print('读取成功:${value}');
});
then方法中的onError
then
方法有两个入参,第一个是必传的回调函数,此外还有一个onError
的可选回调,需要注意的是一旦在then
中提供onError
回调后,对then的返回值再监听catchError
将会无效。
async/await关键字的使用
当有多个异步任务需要按顺序执行的时候then
就会显得有些臃肿,为了解决这个问题引入async/await
这个关键字组合,await
关键字只能在async
修饰的方法中引用,且用await
修饰的Future
对象,必须等待上个异步对象完成后,才可以执行下一行代码。
js
void readFile(TaskResult result) async{
String filePath = path.join(Directory.current.path,"config","config.json");
File configFile = File(filePath);
String configContent = await configFile.readAsString(); // tag1
String assetsPath = json.decode(configContent)['assets_file_path'];
File assetsFile = File(assetsPath);
String assetsContent = await assetsFile.readAsString();
print(assetsContent);
dynamic map = json.decode(assetsContent);
int count = map['read_count'];
map['read_count'] = count+1;
File resultFile = await assetsFile.writeAsString(json.encode(map));
print("写入成功:${resultFile.path}");
}
但是需要注意的是,await
修饰的异步任务结束后才会继续向下执行,因此只有之后的逻辑和任务结果相关时,才有使用的价值,如果两个异步任务没有任何关系,当使用await
修饰第一个异步任务时,第二个异步任务必须等到第一个完成才可以开始执行,这是完全没有必要的,反而会起到阻塞的反效果。