美文网首页
Dart 中 Async 和 await 的理解

Dart 中 Async 和 await 的理解

作者: 11amok | 来源:发表于2020-12-18 17:24 被阅读0次

在弄清Async 和 await之前,首先要清楚Dart是单线程模型,并不是靠子线程实现的异步操作。Async 和 await 实现的异步只适合耗时操作为等待类型的,例如接口请求,时间都耗费在等待上,等待期间,线程可以干别的事情。如果耗时操作是密集计算型,是不适用的,会一直等待直到计算完毕。因为至始自终,都是单线程运行。这里涉及到Dart的事件循环模型:Event loops

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方法中使用。
444.PNG
2.async方法中在await前面的代码会立即同步执行,直到碰到await。
222.PNG
3.await的作用是等待所标记的方法(即getHttp)获取返回结果。
333.PNG
await getHttp()//这个表达式就是返回的String: "请求到的数据:" +  result.body.toString()
//去掉await的话 , getHttp()会立即返回一个Future,不会在此等待

代码跑到await getHttp()时,线程并没有阻塞,getHttp的代码是会继续执行(发起请求,但还没有获取到结果)。await后面的代码不会执行,直到getHttp返回结果才恢复执行。

4.当代码执行到await,会立即返回一个future。
111.PNG

也就是说执行完getHttp后(尚未返回结果)会立马给_startMethod 方法返回一个future。然后继续执行main剩下的代码
类似kotlin的非阻塞挂起,线程并没有阻塞,它会继续执行main方法后面的代码,即print("C开始"); 。当请求结果返回时, await后面的代码才恢复执行,打印返回的http报文 和 print("A执行结束"),至此整个程序结束。

接下来再看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方法加入耗时操作,会发生什么呢?
main() {
  _startMethod();
  print("C开始");
  sleep(Duration(seconds: 10));
}

实验结果是打印"C开始"后,就阻塞了10秒,10秒后才打印出后续的http结果。这里哪怕接口已经返回了也没有立即执行打印,为什么呢?
由dart的EventLoop 事件模型可知,main方法执行完后才会从EventLoop持续获取事件并执行。main方法睡眠这10秒内是真正阻塞了,哪怕接口的future有了返回值,也只是发送了一个事件到EventLoop排队等候处理。等main方法执行完空闲后,才会取出该事件,并打印http的返回结果。
由此可见,dart的await异步是单线程的,只有线程空闲了才会执行异步结果的处理。

问题2:假如去掉main里的sleep,把 http.get方法换成sleep10秒操作,会发生什么呢?
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能改变代码顺序

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执行结束

关键点:

1.Dart中 await的异步是单线程的异步,执行到await时,await之前和await所标记的方法是会立即执行的(例如发起请求操作)。然后立马返回一个Future,继续执行外层剩余的代码。await后面的代码是不会执行的,只有future返回了结果才恢复执行await后面代码。这样就实现了异步的效果。

2.一组async 和 await 其实涉及到了 2个future, await 关键字右边的方法就是一个future,async方法返回的也一定是个future。await标记的future执行完毕时,才继续执行await后面的代码。后面继续执行到return时,async方法返回的future才算执行完毕。

3.如果await 所标记的方法是等待类型的,那么等待期间可以继续运行外层的代码。
如果await所标记的方法是计算密集的,那么就会阻塞在这里,等计算完毕再运行外层代码,无法实现异步效果。

4.await会改变执行顺序。因为await后面的代码已经作为一个event发送到了Event Loops中。会先执行外层剩余的main方法,再执行这个事件。至于是等待期间就执行main还是等待完才执行main,取决于上面的第3点

最后可以再试试这个例子: 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 "请求到的数据:";
}

参考:
dart 事件模型 : https://medium.com/dartlang/dart-asynchronous-programming-isolates-and-event-loops-bffc3e296a6a
Future: https://medium.com/dartlang/dart-asynchronous-programming-futures-96937f831137
Async、await https://dart.dev/codelabs/async-await
Dart 异步: https://medium.com/dartlang/dart-asynchronous-programming-futures-96937f831137

相关文章

网友评论

      本文标题:Dart 中 Async 和 await 的理解

      本文链接:https://www.haomeiwen.com/subject/kuaxnktx.html