Dart Async 、 await 、Future 特性
在弄清Async
和 await
之前,首先要清楚Dart是单线程模型,并不是靠子线程实现的异步操作。Async
和 await
实现的异步只适合耗时操作为等待类型的,例如接口请求,时间都耗费在等待上,等待期间,线程可以干别的事情。如果耗时操作是计算密集型,是不适用的,计算时会一直阻塞直到计算完毕。因为至始自终,都是单线程运行。这里涉及到Dart的事件循环模型:Event loops
让我们看一段示例:
代码很简单,_startMethod
间接调用了一个网络请求, 返回一串html。
import 'dart:io';
import 'package:http/http.dart' as http;
main() {
_startMethod();
print("C开始");
}
Future _startMethod() async{
print("A开始执行这个方法~");
print(await getHttp());
print("A执行结束");
}
Future<String> getHttp() async {
final result = await http.get('https://www.jianshu.com/');
return "请求到的数据:" result.body.toString();
}
执行结果 :接口等待期间,执行了main
方法剩余的代码,打印了“C开始”
,然后才打印接口返回数据。实现了异步处理效果。
I/flutter (22228): A开始执行这个方法~
I/flutter (22228): C开始
I/flutter (22228): 请求到的数据:<!DOCTYPE html>………………此处省略返回的HTML
I/flutter (22228): A执行结束
这里有两层 async
和 await
的组合,为了便于理解,我们先只看第一层组合即_startMethod
方法。先不管 getHttp
里面的代码,只需要知道它发起了一个请求,返回值是请求的结果,这个方法是需要耗时等待的。
查阅官方文档得知,归纳出以下几点:
1. await
必须在async
方法中使用。
2.async
方法中在await
前面的代码会立即同步执行,直到碰到await
。
3.await
的作用是等待所标记的方法(即getHttp
)获取返回结果。
await getHttp()//这个表达式就是返回的String: "请求到的数据:" result.body.toString()
//去掉await的话 , getHttp()是返回一个Future,不会在此等待,会立即执行后续的代码
代码跑到await getHttp()
时,getHttp
的代码是会执行(发起请求,但还没有获取到结果)。await
后面的代码不会执行,直到getHttp
返回结果才恢复执行。
4.当代码执行到await
,会立即返回一个future
。
也就是说执行完getHttp
后(尚未返回结果)会立马给_startMethod
方法返回一个future
。然后继续执行main
剩下的代码,即print("C开始")
; 。当请求结果返回时, await
后面的代码才恢复执行,打印返回的http报文 和 print("A执行结束")
,至此整个程序结束。 整个过程类似kotlin
协程的非阻塞挂起。
接下来再看getHttp
方法,加上两处打印
Future<String> getHttp() async {
print("B开始执行这个方法~");
final result = await http.get('https://www.jianshu.com/');
print("B 获取Http 结束~");
return "请求到的数据:" result.body.toString();
}
执行的结果如下:
I/flutter (22228): A开始执行这个方法~
I/flutter (22228): B开始执行这个方法~
I/flutter (22228): C开始
I/flutter (22228): B 获取Http 结束~
I/flutter (22228): 请求到的数据:<!DOCTYPE html> ……此处省略HTML
I/flutter (22228): A执行结束
这里同理,会先执行 print("B开始执行这个方法~") , 运行await处时,会执行http.get方法,await后面的都不再执行。并立即返回一个Future给上一级。类似一个递归的思想,接口返回后,恢复await也是先恢复的内层的getHttp,再恢复外层的_startMethod。
拓展几个问题,以了解await
运行的特性:
问题1:假如在main
方法加入耗时操作: sleep(Duration(seconds: 10))
,会发生什么呢?
main() {
_startMethod();
print("C开始");
sleep(Duration(seconds: 10));
}
实验结果是打印"C开始"
后,就阻塞了10秒,10秒后才打印出后续的http结果。这期间哪怕接口已经返回了数据也没有立即恢复执行await后面的代码,为什么呢? 由dart
的EventLoop
事件模型可知,main
方法执行完后才会从EventLoop
持续获取事件并执行。接口的future
有了返回值,只是发送了一个事件到EventLoop
排队等候处理。由于此时线程并不空闲,正在执行10秒睡眠,并没有取出事件执行。直到main
方法执行完处于空闲状态,才会取出该事件,打印http的返回结果。 由此可见,dart的await异步是单线程的,只有线程空闲了才会恢复执行await后面代码。
问题2:假如去掉main
里的sleep
,把 http.get
方法换成sleep
10秒操作,会发生什么呢?
Future<String> getHttp() async {
print("B开始执行这个方法~");
await sleep(Duration(seconds: 10));
print("B 睡眠 结束~");
return "请求到的数据:xxxxxx" ;
}
结果如下:打印 “B开始执行这个方法” 后会等待10秒。等待期间并没有执行别的代码。
I/flutter (22228): A开始执行这个方法~
I/flutter (22228): B开始执行这个方法~ // 这里会等待10秒
I/flutter (22228): C开始
I/flutter (22228): B 获取Http 结束~
I/flutter (22228): 请求到的数据:xxxxxx
I/flutter (22228): A执行结束
这里的sleep是阻塞的,相当于计算密集型操作, 整个线程阻塞在这里, 等待期间main
方法剩余的代码也不会执行,直到10秒结束。
问题3:假如去掉问题2中的await
关键字会如何?
Future<String> getHttp() async {
print("B开始执行这个方法~");
sleep(Duration(seconds: 3));
print("B 睡眠 结束~");
return "请求到的数据:xxxxxx" ;
}
结果如下:“C开始”
和 “B 获取Http 结束~”
换了位置。没有await
, 整个getHttp()
的代码都会顺序执行。也就是说await能改变代码执行顺序,因为执行到await
会立即返回一个future
,等待期间继续先执行外层的代码。
I/flutter (22228): A开始执行这个方法~
I/flutter (22228): B开始执行这个方法~ //等待10秒
I/flutter (22228): B 获取Http 结束~
I/flutter (22228): C开始
I/flutter (22228): 请求到的数据:xxxxxx
I/flutter (22228): A执行结束
关键点:
-
Dart
中await
的异步是单线程的异步,执行到await
时,await
之前和await
所标记的方法是会立即执行的(例如发起请求操作)。然后立马返回一个Future
,继续执行外层剩余的代码。await
后面的代码是不会执行的,只有future
返回了结果才恢复执行await
后面代码。这样就实现了异步的效果。 -
一组
async
和await
其实涉及到了 2个future
,await
关键字右边的方法就是一个future
,asyn
c方法返回值也一定是个future
。await
标记的future
执行完毕时,才继续执行await
后面的代码。后面继续执行到return
时,async
方法返回的future
才算执行完毕。 -
如果
await
所标记的方法是等待类型的,那么等待期间可以继续运行外层的代码。 -
如果
await
所标记的方法是计算密集的,那么就会阻塞在这里,等计算完毕再运行外层代码,无法实现异步效果。 -
await
会改变执行顺序。因为await
后面的代码已经作为一个event
发送到了Event Loops
中。会先执行外层剩余的main
方法,再执行这个事件。至于是等待期间就执行main还是等待完才执行main,取决于上面的第3、4点。
最后可以再试试这个例子: compute
和 sleep
的效果是一样的,会一直阻塞,而网络请求则可以在等待期间返回继续跑外层的代码。
Future<String> getHttp() async {
print("B开始执行这个方法~");
// final result = await http.get('https://www.jianshu.com/');
await compute();
print("B 获取Http 结束~");
return "请求到的数据:";
}
Future<String> compute() async {
int b = 0;
for(int i= 0 ; i < 1000000000 ; i ){
b ;
}
return "compute数据:";
}
参考:
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanheggekh
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13