• Flutter异步Future


    一、认识Future

    1.创建Future

    void testFuture(){
      Future future = new Future(() => null);
      future.then((_){
        print("then");
      }).then((){
        print("whenComplete");
      }).catchError((_){
        print("catchError");
      });
    } 

    这里的执行结果是:

    then
    whenComplete
    Futue直接new就可以了。
    我这里没有具体的返回数据,所以就用匿名函数代替了, Future future = new Future(() => null);相当于 Future<Null> future = new Future(() => null);
    泛型如果为null可以省略不写,为了便于维护和管理,开发中建议加上泛型。
     
    我们也可以直接在创建Future的时候直接输出一些内容,例如:
    void testFuture2(){
      Future f1 = new Future(() => print("1"));
      Future f2 = new Future(() => print("2"));
      Future f3 = new Future(() => print("3"));
    }

    输出结果是:

    1
    2
    3

    2.Future相关回调函数

    future里面有几个函数:
    then:异步操作逻辑在这里写。
    whenComplete:异步完成时的回调。
    catchError:捕获异常或者异步出错时的回调。

    因为这里面的异步操作过程中没有遇到什么错误,所以catchError回调不会调用。

    在我们平时开发中我们是这样用的,首先给我们的函数后面加上async关键字,表示异步操作,然后函数返回值写成Future,然后我们可以new一个Future,逻辑前面加上一个await关键字,然后可以使用future.then等操作。下面是一个示例操作,为了方便演示,这里的返回值的null。平时开发中如果请求网络返回的是json,我们可以把泛型写成String;泛型也可能是实体类(entity/domain),不过要做json转实体类相关操作。
    Future asyncDemo() async{
        Future<Null> future = new Future(() => null);
        await  future.then((_){
          print("then");
        }).then((){
          print("whenComplete");
        }).catchError((_){
          print("catchError");
        });
    }

    二、创建多个Future的执行步骤

    1.我们这里创建3个Future,我们看看执行过程:

    void testFutureCreate1(){
      Future f1 = new Future(() => null);
      Future f2 = new Future(() => null);
      Future f3 = new Future(() => null);
      // 都是异步 空实现 顺序进行
      f1.then((_) => print("1"));
      f2.then((_) => print("2"));
      f3.then((_) => print("3"));
    }

    我们可以看到执行结果是:

    1
    2
    3

    2.那么我们猜想是不是按照创建Future对象的先后顺序依次执行? 接下来我们打乱顺序,再试试看!

    void testFutureCreate2(){
      Future f2 = new Future(() => null);
      Future f1 = new Future(() => null);
      Future f3 = new Future(() => null);
      f1.then((_) => print("1"));
      f2.then((_) => print("2"));
      f3.then((_) => print("3"));
    }
    2
    1
    3

    我们可以看到输出结果是: 2 1 3 和我们创建Future对象的先后顺序完全一致。

    3.接下来我们继续猜想打乱then的调用先后顺序试试看?会不会有影响?

    void testFutureCreate3(){
    
      Future f1 = new Future(() => null);
      Future f2 = new Future(() => null);
      Future f3 = new Future(() => null);
    
      f3.then((_) => print("3"));
      f1.then((_) => print("1"));
      f2.then((_) => print("2"));
    
    }

    我们可以看到结果为1 2 3,和我们调用then的先后顺序无关。:

    1
    2
    3

    4.【结论】: 创建多个Future,执行顺序和和创建Future的先后顺序有关,如果只是单独的调用then,没有嵌套使用的话,和调用then的先后顺序无关。

    三、then函数嵌套使用的执行步骤

    当then回调函数里面还有then回调的时候,这时候的流程跟前面就不太一样了,也是一个大坑,也是面试经常会被问到的一个知识点。

    1.我们一开始就执行f1的then回调,接着是f2的then回调里面,包含一个f1的then回调,最后是f3的then回调。示例如下:

    void testThen1() {
      Future f1 = new Future(() => null);
      Future f2 = new Future(() => null);
      Future f3 = new Future(() => null);
    
      f1.then((_) => print("f1 -> f1"));
      // f2 then 异步回调里面还有异步回调
      f2.then((_) {
        print("f2 -> f2");
        f1.then((_) => print("f2.then -> f1"));
      });
      f3.then((_) => print("f3 -> f3"));
    }

    我们可以看到执行结果如下:

    f1 -> f1
    f2 -> f2
    f2.then -> f1
    f3 -> f3

    2.接下来我们交换一下调用then的顺序。我们发现结果是一样的:

    void testThen2() {
      Future f1 = new Future(() => null);
      Future f2 = new Future(() => null);
      Future f3 = new Future(() => null);
    
      f2.then((_) {
        print("f2 -> f2");
        f1.then((_) => print("f2.then -> f1"));
      });
      f1.then((_) => print("f1 -> f1"));
      f3.then((_) => print("f3 -> f3"));
    }

    结果还是一样的:

    f1 -> f1
    f2 -> f2
    f2.then -> f1
    f3 -> f3

    3.接下来我们调整一下。

    void testThen3() {
      Future f1 = new Future(() => null);
      Future f2 = new Future(() => null);
      Future f3 = new Future(() => null);
    
      f1.then((_) => print("f1 -> f1"));
      f3.then((_) {
        print("f3 -> f3");
        f1.then((_) => print("f3.then -> f1"));
      });
      f2.then((_) => print("f2 -> f2"));
    }

    运行结果是:

    f1 -> f1
    f2 -> f2
    f3 -> f3
    f3.then -> f1

    这里再次证明了上面我的猜想:执行顺序和和创建Future的先后顺序有关,如果有多个then嵌套执行,先执行外面的then,然后执行里面的then。

    4. 接下来我们在then内部创建一个Future 看看执行顺序如何?

    void testThen4() {
      Future f1 = new Future(() => null);
      Future f2 = new Future(() => null);
      Future f3 = new Future(() => null);
    
      f1.then((_) => print("f1 -> f1"));
      f3.then((_) {
        print("f3 -> f3");
        new Future(() => print("f3.then -> new Future"));
        f1.then((_) => print("f3.then -> f1"));
      });
      f2.then((_) => print("f2 -> f2"));
    }

    执行结果如下,我们可以看到then内部创建的Future要等到then执行完了,最后再去执行的:

    f1 -> f1
    f2 -> f2
    f3 -> f3
    f3.then -> f1
    f3.then -> new Future

    5.【结论】: 首先执行顺序和创建Future的先后顺序有关,如果遇到多个 then 嵌套,先执行外面的 then,然后再执行里面的then,如果then里面还有创建Future,要等到then执行完毕,之后执行Future。

    四、综合示例

    void testAll() {
      Future f3 = new Future(() => null);
      Future f1 = new Future(() => null);
      Future f2 = new Future(() => null);
      Future f4 = new Future(() => null);
      Future f5 = new Future(() => null);
    
      f3.then((_) => print("f3.then -> f3"));
      f2.then((_) => print("f2.then -> f2"));
      f4.then((_) => print("f4.then -> f4"));
    
      f5.then((_) {
        print("f5.then -> f5");
        new Future(() => print("f5.then -> new Future"));
        f1.then((_) {
          print("f1.then -> f1");
        });
      });
    }

    根据上文总结的特点,我们可以不用运行也能推断出输出结果:

    首先按照Future创建顺序应该是 f3 f1 f2 f4 f5依次执行。
    我们看到then函数的调用情况,f3先调用,所以它应该先输出。
    紧接着是f2调用了,所以f2输出。
    紧接着f4调用了,f4应该输出。
    紧接着是f5调用then函数,这个比较特殊,它是then函数的嵌套使用,首先是一个打印语句,直接输出,然后是new Future函数,它应该等then执行完毕再去执行,所以这里会去找下面的f1.then里面的逻辑,然后输出f1,到此f5.then都执行完毕,然后就是执行new Future里面的逻辑(如果没有内部嵌套 then的话,它就会直接输出。)
    为了验证我们的猜想,我们打印一下输出结果,果然我们的证明是正确的。
    f3.then -> f3
    f2.then -> f2
    f4.then -> f4
    f5.then -> f5
    f1.then -> f1
    f5.then -> new Future

    五、我们来看看Future的源码说明文档

    我们重点看看then函数的文档说明:

    then注册在 Future 完成时调用的回调。
    当这个 Future 用一个 value 完成时,将使用该值调用onValue回调。
    如果 Future已经完成,则不会立即调用回调,而是将在稍后的microtask(微任务)中调度。
    如果回调返回Future,那么then返回的future将与callback返回的future结果相同。

    onError回调必须接受一个参数或两个参数,后者是[StackTrace]。

    如果onError接受两个参数,则使用错误和堆栈跟踪时调用它,否则仅使用错误对象时候调用它。

    onError回调必须返回一个可用于完成返回的future的值或future,因此它必须是可赋值给FutureOr <R>的东西。

    返回一个新的Future,该Future是通过调用onValue(如果这个Future是通过一个value完成的)或'onError(如果这个Future是通过一个error完成的)的结果完成的。

    如果调用的回调抛出异常,返回的future将使用抛出的错误和错误的堆栈跟踪完成。在onError的情况下,如果抛出的异常与onError的错误参数“相同(identical)”,则视为重新抛出,并使用原始堆栈跟踪替代

    如果回调返回Future,则then返回的Future将以与回调返回的Future相同的结果完成。

    如果未给出onError,并且后续程序走了刚出现了错误,则错误将直接转发给返回的Future

    在大多数情况下,单独使用catchError更可读,可能使用test参数,而不是在单个then调用中同时处理valueerror

    请注意,在添加监听器(listener)之前,future不会延迟报告错误。如果第一个thencatchError调用在future完成后发生error,那么error将报告为未处理的错误。

  • 相关阅读:
    JS组件系列——自己动手扩展BootstrapTable的treegrid功能
    JS组件系列——自己动手封装bootstrap-treegrid组件
    JS组件系列——又一款MVVM组件:Vue(二:构建自己的Vue组件)
    使用Advanced Installer制作IIS安装包(二:配置安装包依赖项和自定义dll)
    使用Advanced Installer制作IIS安装包(一:配置IIS和Web.config)
    C#组件系列——又一款日志组件:Elmah的学习和分享
    推荐一款带暂停功能的轮播组件,不要谢我,我叫红领巾!
    JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)
    CSS系列——前端进阶之路:初涉Less
    MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
  • 原文地址:https://www.cnblogs.com/mingfeng002/p/11820874.html
Copyright © 2020-2023  润新知