• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Dart Async 、 await 、Future 特性

武飞扬头像
11amok
帮助3

在弄清Asyncawait之前,首先要清楚Dart是单线程模型,并不是靠子线程实现的异步操作。Asyncawait 实现的异步只适合耗时操作为等待类型的,例如接口请求,时间都耗费在等待上,等待期间,线程可以干别的事情。如果耗时操作是计算密集型,是不适用的,计算时会一直阻塞直到计算完毕。因为至始自终,都是单线程运行。这里涉及到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执行结束

这里有两层 asyncawait 的组合,为了便于理解,我们先只看第一层组合即_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后面的代码,为什么呢? 由dartEventLoop 事件模型可知,main方法执行完后才会从EventLoop持续获取事件并执行。接口的future有了返回值,只是发送了一个事件到EventLoop排队等候处理。由于此时线程并不空闲,正在执行10秒睡眠,并没有取出事件执行。直到main方法执行完处于空闲状态,才会取出该事件,打印http的返回结果。 由此可见,dart的await异步是单线程的,只有线程空闲了才会恢复执行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能改变代码执行顺序,因为执行到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执行结束

关键点:

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

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

  3. 如果await 所标记的方法是等待类型的,那么等待期间可以继续运行外层的代码。

  4. 如果await所标记的方法是计算密集的,那么就会阻塞在这里,等计算完毕再运行外层代码,无法实现异步效果。

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

最后可以再试试这个例子: computesleep 的效果是一样的,会一直阻塞,而网络请求则可以在等待期间返回继续跑外层的代码。

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
系列文章
更多 icon
同类精品
更多 icon
继续加载