Dart类库有非常多的返回Future或者Stream对象的函数,这些函数被称为异步函数。
1. Future
Future与JavaScript中的Promise非常相似,表示一个异步操作的最终完成(或失败)及其结果值的表示。异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果,要么成功,要么失败。
还有,Future 的所有API的返回值仍然是一个Future对象。
1)Future.then
为了方便示例,在本例中我们使用Future.delayed 创建了一个延时任务(实际场景会是一个真正的耗时任务,比如一次网络请求),即2秒后返回结果字符串"hi world!",然后我们在then中接收异步结果并打印结果,代码如下:
Future.delayed(const Duration(seconds: 2), () {
return "hi world";
}).then((value) => print(value));
2)Future.catchError
如果异步任务发生错误,我们可以在catchError中捕获错误,我们将上面示例改为:
Future.delayed(const Duration(seconds: 2), () {
//return "hi world!";
throw AssertionError("Error");
}).then((data) {
//执行成功会走到这里
print("success");
}).catchError((e) {
//执行失败会走到这里
print(e);
});
在本例中,我们在异步任务中抛出了一个异常,then的回调函数将不会被执行, catchError回调函数将被调用;但是,并不是只有 catchError回调才能捕获错误,then方法还有一个可选参数onError,我们也可以用它来捕获异常:
Future.delayed(const Duration(seconds: 2), () {
//return "hi world!";
throw AssertionError("Error");
}).then((data) {
print("success");
}, onError: (e) {
print(e);
});
3)Future.whenComplete
有些时候,我们会遇到无论异步任务执行成功或失败都需要做一些事的场景,比如:在网络请求前弹出加载对话框,在请求结束后关闭对话框,这种场景,有两种方法:
1.分别在then或catch中关闭一下对话框
2.使用Future的whenComplete回调
Future.delayed(const Duration(seconds: 2), () {
//return "hi world!";
throw AssertionError("Error");
}).then((data) {
//执行成功会走到这里
print(data);
}).catchError((e) {
//执行失败会走到这里
print(e);
}).whenComplete(() {
//无论成功或失败都会走到这里
});
4)Future.wait
有些时候,我们需要等待多个异步任务都执行结束后才进行一些操作,比如我们有一个界面,需要先分别从两个网络接口获取数据,获取成功后,我们需要将两个接口数据进行特定的处理,再显示到UI界面上,这时可以用Future.wait,它接受一个Future数组参数,只有数组中所有Future都执行成功后,才会触发then的成功回调,只要有一个Future执行失败,就会触发错误回调。
Future.wait([
// 2秒后返回结果
Future.delayed(const Duration(seconds: 2), () {
return "hello";
}),
// 4秒后返回结果
Future.delayed(const Duration(seconds: 4), () {
return "world";
})
]).then((result) {
print(result[0] + result[1]);
}).catchError((e) {
print(e);
});
执行上面代码,4秒后你会在控制台中看到“hello world”。
2.async/await
正如上文所述, “Future 的所有API的返回值仍然是一个Future对象” ,如果在then 中返回的是一个Future的话,该future会执行,执行结束后会触发后面的then回调,这样依次向下,就避免了层层嵌套。
login("alice","******").then((id){
return getUserInfo(id);
}).then((userInfo){
return saveUserInfo(userInfo);
}).then((e){
//执行接下来的操作
}).catchError((e){
//错误处理
print(e);
});
Dart中的async/await的意思就是:异步任务串行化,可以避免我们在使用过程中遇到的回调地狱问题。
task() async {
try {
String? id = await login("admin", "xxx");
String? userInfo = await getUserInfo(id!);
await saveUserInfo(userInfo!);
//执行接下来的操作
} catch(e) {
//错误处理
print(e);
}
}
3.Stream
Stream也是用于接收异步事件数据,和 Future不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。
Stream.fromFutures([
// 1秒后返回结果
Future.delayed(const Duration(seconds: 1), () {
return "hello 1";
}),
// 抛出一个异常
Future.delayed(const Duration(seconds: 2), () {
throw AssertionError("Error");
}),
// 3秒后返回结果
Future.delayed(const Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data) {
print(data);
}, onError: (e) {
print(e.message);
}, onDone: () {
});
上面的代码依次会输出:
2023-03-28 15:12:10.356 6222-6280 flutter com.example.hello_flutter I hello 1
2023-03-28 15:12:11.364 6222-6280 flutter com.example.hello_flutter I Error
2023-03-28 15:12:12.354 6222-6280 flutter com.example.hello_flutter I hello 3