异步
Future
Dart 里面的异步,和 Java 里面的异步有些区别,要实现异步操作,我们就需要用到 Future 这个关键字,Future 将来的意思,可以放一个泛型 Future<T>
,做为一个返回值,当线程完毕后我们能拿到这个对象,而 Future 对象里面同样有一些静态方法也是可以有异步的效果:
void main() {
print("start time ${DateTime.now()}");
Future.delayed(new Duration(seconds:2),(){
print("delay nowTime = ${DateTime.now()}");
});
print("end time ${DateTime.now()}");
}
根据代码是按照顺序执行的原理,我们想的打印结果应该为:
1.start time
延迟两秒后打印
2.delay nowTime
3.end time
我们运行起来看看结果:
start time 2021-01-28 15:39:23.941454
end time 2021-01-28 15:39:23.948424
delay nowTime = 2021-01-28 15:39:25.952542
可以看到,delay nowTime 是最后打出来的,说明Future.delayed
和他前后两个print
并不是同步进行
那么我们要如何才能让他按照我们的预期,同步进行呢?那就是 then
方法,修改代码如下:
void main() {
print("start time ${DateTime.now()}");
Future.delayed(new Duration(seconds:2),(){
print("delay nowTime = ${DateTime.now()}");
}).then((value) => print("end time ${DateTime.now()}"));
}
理解一下 then 这个方法,然后的意思,根据意思来看就是延迟两秒后打印时间,然后再打印结束时间,我们运行看看效果:
start time 2021-01-28 15:49:15.639934
delay nowTime = 2021-01-28 15:49:17.652054
end time 2021-01-28 15:49:17.653041
确实,按照我们的预期,打印出了我们想要的结果。
我们在看,delayed,then
都有返回结果,那么我们将打印返回,在结束的时候再看看结果如何修改代码如下:
void main() {
print("start time = ${DateTime.now()}");
Future.delayed(new Duration(seconds:2),(){
return "delay nowTime = ${DateTime.now()} ";
})
.then((value) => value + " end time = ${DateTime.now()}")
.then((value) => print(value));
}
我们将打印作为返回值,运行程序,打印结果如下:
start time 2021-01-28 15:55:21.855428
delay nowTime = 2021-01-28 15:55:23.869513 end time 2021-01-28 15:55:23.870512
可以看出,每次的返回值都会成为 then
的参数传入下一个 then
。
同样我们在这样链式的调用中,也会有异常抛出,那么这样的异常我们应该怎么取捕获呢?Future 给了我们一个静态方法catchError(e)
,我们修改代码如下:
void main() {
print("start time = ${DateTime.now()}");
Future.delayed(new Duration(seconds:2),(){
return "delay nowTime = ${DateTime.now()} ";
})
.then((value) => value + " end time = ${DateTime.now()}")
.then((value) => throw new Exception(value))
.catchError((onError){
print(onError);
});
}
在最后我们将我们拼接的时间字符串当一个异常抛出去,用我们的catchError(e)
去捕获,运行程序如下:
start time = 2021-01-28 16:00:29.391334
Exception: delay nowTime = 2021-01-28 16:00:31.407588 end time = 2021-01-28 16:00:31.408584
可以看到异常被我们捕获了。
好了,现在我们需要做一个网络请求,模拟一个下载操作,我们需要定义自己的异步代码块该怎么做呢?使用 Future 和 async 关键字模拟一个网络下载操作吧。
void main() {
print("程序开始");
var downloadUrl = requestDownloadUrl();
var result = download(downloadUrl);
print("下载结果:$result");
print("程序结束");
}
Future<String> requestDownloadUrl() async {// 请求下载地址
print("开始请求下载地址....");
return "www.baidu.com";
}
Future<String> download(var downloadUrl) async{// 开始下载文件
print("开始进行下载操作,下载地址为:$downloadUrl");
return "下载成功";
}
我们创建了两个方法,一个是请求下载地址,一个开始下载文件,而且开始下载文件这个方法需要请求下载地址成功后的结果,两个都是耗时操作,我们用 Future 和 async 将他们定义为异步方法。然后我们在 main 函数中依次调用,我们想要的结果应该是 “程序开始 -> 开始请求下载地址…
-> 开始进行下载操作,下载地址为:www.baidu.com -> 下载成功 程序结束
” 运行程序打印结果如下:
程序开始
开始请求下载地址....
开始进行下载操作,下载地址为:Instance of 'Future<String>'
下载结果:Instance of 'Future<String>'
程序结束
顺序是我们想要的顺序,但是下载地址和下载结果返回的是 Future 对象,因为是异步,获取真正的结果是在未来的一段时间,所以我们需要使用then
方法去获取我们异步方法得到的结果,修改程序如下:
void main() {
print("程序开始");
requestDownloadUrl()
.then((value) => download(value))
.then((value) => print(value + " 程序结束"));
}
Future<String> requestDownloadUrl() async {
print("开始请求下载地址....");
return "www.baidu.com";
}
Future<String> download(var downloadUrl) async{
print("开始进行下载操作,下载地址为:$downloadUrl");
return "下载成功";
}
我们将代码进行链式调用,运行,打印结果如下:
程序开始
开始请求下载地址....
开始进行下载操作,下载地址为:www.baidu.com
下载成功 程序结束
和我们预期一致。
网上搜索一下 Dart 的异步在 2.0 以下都是用的是 Future then 的方式,现在都是 2.0 以上了,都开始用 async,await,的方式了。好家伙,学了半天,学了个过时的。
async、await
那就继续吧,翻了翻资料,理解理解,发现async , await 的效果好像跟 then 的效果一样,只是这个方式方便对阅读代码。OK,那么改改上面的代码吧:
void main() async {
print("程序开始");
String downloadUrl = await requestDownloadUrl();
String result = await download(downloadUrl);
print(result);
print("程序结束");
}
Future<String> requestDownloadUrl() async {
print("开始请求下载地址....");
return "www.baidu.com";
}
Future<String> download(var downloadUrl) async {
print("开始进行下载操作,下载地址为:$downloadUrl");
return "下载成功";
}
其实也比较简单,在异步方法前面加个 await 等待异步方法调用完毕,返回结果就行,但是 await 又是和 async 成对使用的,所以需要在 await 使用的方法外加上 async。OK,代码修改完毕运行一下看看打印结果:
程序开始
开始请求下载地址....
开始进行下载操作,下载地址为:www.baidu.com
下载成功
程序结束
妥妥的。