zoukankan      html  css  js  c++  java
  • flutter中的异步机制 Future

    饿补一下Flutter中Http请求的异步操作。

    Dart是一个单线程语言,可以理解成物理线路中的串联,当其遇到有延迟的运算(比如IO操作、延时执行)时,线程中按顺序执行的运算就会阻塞,用户就会感觉到卡顿,于是通常用异步处理来解决这个问题。

    Dart异步编程有两种方式:Future和Stream

    Future相当于40米大砍刀,Stream相当于一捆40米大砍刀。dart提供了关键字async(异步)和await(延迟执行),相当于普通的便捷的小匕首,而小匕首是我们平时经常用到的。

    当遇到有需要延迟的运算(async)时,将其放入到延迟运算的队列(await)中去,把不需要延迟运算的部分先执行掉,最后再来处理延迟运算的部分。

    1、async和await

    async await 这两个关键字是dart语言的特性,能让你写出看起来像是“同步”的“异步”代码,先看一个方法案例:

      /*HTTP的get请求返回值为Future<String>类型,即其返回值未来是一个String类型的值*/
      /*async关键字声明该函数内部有代码需要延迟执行*/
      getData() async {    
        /*await关键字声明运算为延迟执行,然后return运算结果*/
        return await http.get(Uri.encodeFull(url), headers: {"Accept": "application/json"}); 
      }
    

    然后我们尝试调用这个方法,并获取返回值。

    String data = getDate();
    

    然后控制台报错了….

    为什么呢?因为data是String类型,而函数getData()是一个异步操作函数,其返回值是一个await延迟执行的结果。在Dart中,有await标记的运算,其结果值都是一个Future对象,Future不是String类型,所以就报错了。

    总结一下:

    在请求方法中直接 return await .. .的时候,实际上返回的是一个延迟计算的Future对象。

    还有两点需要注意:

    • await关键字必须在async函数内部使用
    • 调用async函数必须使用await关键字

    后面两点怎么讲?

    1. 要想 return await … ,那么方法首先是 async 的,如上方方法。
    2. 使用 async 标注的方法,必须要用 await 接收返回值,比如上方方法在接收返回值时,需要加入 await ,var data = await getData();

    2、什么是Future

    Future表示一件“将来”会发生的事情,将来可以从Future中取到一个值。当一个方法返回一个Future的事情,发生两件事情:

    • 这个方法将某件事情排队,返回一个未完成的Future
    • 当这件事情完毕之后,Future的状态会变成已完成,这个时候就可以取到这件事情的返回值了。

    要取到这个“返回值”,有两种方式:

    • 使用async配合await
    • 使用Future提供的api

    我们看这两种实现方式的案例:

    2.1、使用async配合await

    先看个案例,等待3秒后返回‘我是用户’:

    /*模拟异步加载用户信息*/
    Future _getUserInfo() async{
      await new Future.delayed(new Duration(milliseconds: 3000));
      return "我是用户";
    }
    
    /*加载用户信息,顺便打印时间看看顺序*/
    Future _loadUserInfo() async{
      print("_loadUserInfo:${new DateTime.now()}");
      print(await _getUserInfo());
      print("_loadUserInfo:${new DateTime.now()}");
    }
    

    我们在initState中调用该方法:

    @override
    void initState(){
        print("initState:${new DateTime.now()}");
        _loadUserInfo();
        print("initState:${new DateTime.now()}");
    
        super.initState();
    }
    

    打印结果如下:

    I/flutter ( 1802): initState:2019-06-20 09:46:40.097339
    I/flutter ( 1802): _loadUserInfo:2019-06-20 09:46:40.103542
    I/flutter ( 1802): Instance of 'Future<dynamic>'
    I/flutter ( 1802): initState:2019-06-20 09:46:40.108510
    I/flutter ( 1802): 我是用户
    I/flutter ( 1802): _loadUserInfo:2019-06-20 09:46:43.117136
    

    what?

    很明显,打印结果并没有按照串联的方式依次打印。

    flutter中会改造带asyc关键字的方法,让这个方法脱离主流程,变成“后面一点”执行(通过scheduleMicrotask),所以可以让我们的程序“看起来”是顺序执行的。

    2.2、Future api

    我们修改一下 loadUserInfo() 方法:

    /*加载用户信息,顺便打印时间看看顺序*/
      Future _loadUserInfo() async{
        print("_loadUserInfo:${new DateTime.now()}");
        _getUserInfo().then((info){
          print(info);
        });
        print("_loadUserInfo:${new DateTime.now()}");
      }
    

    再次运行输出一下:

    I/flutter ( 1802): initState:2019-06-20 09:50:32.488765
    I/flutter ( 1802): _loadUserInfo:2019-06-20 09:50:32.494751
    I/flutter ( 1802): _loadUserInfo:2019-06-20 09:50:32.499725
    I/flutter ( 1802): Instance of 'Future<dynamic>'
    I/flutter ( 1802): initState:2019-06-20 09:50:32.499970
    I/flutter ( 1802): 我是用户
    

    两次输出是有不同的,主要不同在于第二个 loadUserInfo 的日志打印,与‘我是用户’的输出顺序,为什么有差异?

    await会阻塞流程,等待紧跟着的的Future执行完毕之后,再执行下一条语句,而如果用了Future.then这个api,那么就不会等待,直接执行下面的语句,等Future执行完了,再调用then这个方法。

    3、总结

    在请求方法中直接 return await .. .的时候,实际上返回的是一个延迟计算的Future对象。

    有两点需要注意:

    • await关键字必须在async函数内部使用
    • 调用async函数必须使用await关键字

    Flutter 中有两种实现异步编程的方式:Future api、 async await

    日常开发中常用的是 async await Future 搭配。

  • 相关阅读:
    Vue中v-for不绑定key会怎样
    关于Vuex可直接修改state问题
    不要完全相信Chrome控制台打印的信息
    Vue挂载元素的替换
    (转)openURL的使用方法
    iOS:将NSDate转换为当前时区时间
    OC中使用 static 、 extern、 const使用
    iOS: 正则表达式
    iOS:原生二维码扫描
    iOS:ABPeoplePickerNavigationController系统通讯录使用
  • 原文地址:https://www.cnblogs.com/crazycode2/p/11293728.html
Copyright © 2011-2022 走看看