在之前的文章中,我们已经使用了
Future
。如文件操作、SharedPreferences
都会返回一个Future
。【Flutter 本地数据存储(文件、SharedPreferences 、数据库 sqlite)使用示例】。
dart api
文档中对Future
的描述是表示延迟计算的对象。
在文件操作中使用Future
是因为文件读取是耗时操作,不能马上返回结果,通过Future
延迟之后,我们可以在回调中获取到文件读取之后的结果。
而Future
的回调返回有两种方式,带有值(futuresuccess
)返回或错误(futuresfuture
)返回。
我们可以把Future
想象为一个装有数据的盒子,在一段时间后打开。盒子打开之后,里面有可能有我们想要的东西 value
,也有可能不是我们想要的东西error
。
所以future
就存在三个状态:
value
(成功返回)value
(失败返回)下面根据这三种状态来介绍Future
的使用
Flutter
中很多方法会直接返回Future
对象,例如:
final myFuture = SharedPreferences.getInstance();
有时候我们也会通过构造函数直接new Future
来获取Future
实例。
final myFuture = Future();
创建已知
value
的Future
。当您构建使用缓存的服务时,此构造函数很有用。
使用示例如下:
final myFuture = Future.value(12);
创建已知
error
的Future
。和Future.value()
类似
使用示例如下:
final myFuture = Future.error(ArgumentError.notNull('input'));
创建一个延迟执行的
Future
。在 Flutter 使用 dio 库实现网络请求及 Json 数据解析示例 中我们对dio
进行了二次封装,在showLoading
的时候想显示弹窗,但是这时候showDialog
中的context
还没获取到,所以我们需要延迟之后才显示弹窗。
使用示例如下:
Future.delayed(Duration(milliseconds: 200)).then((e) {
// 延迟之后的操作
});
以上的返回结果都是Future
,也就是完成了Future
的第一个状态,构建。
接着通过then()
就可以获取到我们真正需要的值或者通过catchError()
来捕获错误信息。
处理正确的返回结果。只有单个参数,并且
then
返回的还是Future
对象,因此可以一直链式调用then
方法
该方法返回泛型的onValue
数据,以及可选命名函数onError
,onError
回调必须接受一个或两个参数,如果onError
接受两个参数,则同时使用错误和堆栈跟踪进行调用,否则仅使用错误对象进行调用。
Future then(FutureOr onValue(T value), {Function onError});
Future.delayed(Duration(seconds: 1)).then((value) {
print("第一个 then");
}).then((value) {
print("第二个 then");
});
如果我们在执行的时候遇到了error
可以通过catchError
来捕捉错误信息,当有错误时就不走then
回调,直接走catchError
回调。
它的工作原理与
then()
相同,除了它接受一个error
而不是一个value
,且会返回一个error
的Future
该方法返回onError
回调,以及可选命名函数test
,以在调用回调之前检查错误。
Future catchError(Function onError, {bool test(Object error)});
Future.delayed(Duration(seconds: 3),
() => throw 'Error!', // Complete with an error.
).then((value) {
print(value);
}).catchError((err) {
print('Caught $err'); // Handle the error.
},test: (e){
return e is String;
});
print('Waiting for a value...');
Future
还提供了类似于try-catch-finally
中的finally
块,whenComplete()
方法
不够执行成功或者失败都会执行
whenComplete()
方法,并返回一个Future
。类似于try-catch-finally
中的finally
块。
Future.delayed(Duration(seconds: 3),
() => throw 'Error!', // Complete with an error.
).then((value) {
print(value);
}).catchError((err) {
print('Caught $err'); // Handle the error.
},test: (e){
return e is String;
}).whenComplete((){
print('whenComplete');
});
print('Waiting for a value...');
Flutter
还提供了FutureBuilder widget
用于异步动态更新UI
基于与
Future
交互的widget
,并根据snapshots
中的状态用不同widget
去构建。
既然是个widget
,我们看一下构造函数:
const FutureBuilder({
Key key,
this.future,// Future 对象
this.initialData,// 初始化的数据,在 Future 对象创建完成之前给 snapshots 提供数据
@required this.builder,// 构建器提供了一个 AsyncSnapshot 对象,该对象的 AsyncSnapshot.connectionState 属性有:none(Future 为null),waiting(Future 为等待状态),done(Future 为结束状态)
})
示例:
以上效果源码可查看 FutureBuilder
源码(async.dart
文件)504-562
行。
在使用
Future
的时候,如果有太多异步任务,那么代码的嵌套层级将会很深。dart
中提供了和JavaScript
中一样的方式来解决异步代码层级过深问题,那就是通过async 和 await
关键字。
async 和 await
关键字提供了一种声明性的方式来定义异步函数并使用其结果。
使用async
和await
时,请记住以下两个基本准则:
async
await
关键字仅在异步函数中起作用async
关键字await
关键字接收值async
关键字时需要将返回值转为用Future<原返回值类型>