Flutter开发·Future的理解与使用

2,161 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

之前在找工作时,被问到过两次“你是怎么理解Future的”,其实突然冷不丁的被这么一个抽象的问题一问还挺懵的,其实面试官无非就想看看你平时是怎么用的Future,Future的工作原理是什么样的。今天就总结一下自己对Future的理解以及一些用法。

原理

Future对象可以用来表述一个异步任务的执行,在之前的文章中说,Dart是单线程模型,程序拥有一个Event Loop消息队列,在队列中不断循环查询是否有microtask微任务队列和event queue事件队列,microtask的优先级总是高于event queue。那么在程序中执行到Future时,Dart会将异步任务的函数执行体放入event queue中,然后立即返回去执行后面的代码。当同步执行的代码执行完毕后,event queue会按照加入event queue的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的操作,所以并不会发生阻塞的问题。如: _testFuture方法并没有影响后面print的打印。

2021-10-08 17.42.54.gif

640.gif

async/await

如上面的_testFuture方法加入了async关键字,证明这个函数是异步执行的,执行调用不会阻塞_testFuture方法后面的代码执行。而在方法调用前如果加入了await关键字,则此方法就变成了一个同步方法,需要等待执行结果才可以执行后面的代码,而await和async是成对出现的,有await就一定要有async。

2021-10-08 17.50.52.gif

可以看到print("执行Future后面的代码");这句话是在testFuture方法同步执行完毕后才调用的。

常用方法

Future.value()

表示创建一个返回value中的值得Future,如:

Future _testFuture() async {
  return Future.value("hello world");
}

void main() async{
  print("程序开始");
  String message = await _testFuture();
  print(message);
  print("执行Future后面的代码");
}

控制台:
程序开始
hello world
执行Future后面的代码

Future.delayed()

上面的例子中就用到了此方法用来模拟延时操作,当你需要延时处理时如闪屏页的倒计时跳转,就可以使用该方法。

Future.delayed(Duration(milliseconds: 3000), () {
  /// 跳转至首页
});

Future.then()

Future异步任务结束后的回调,返回值仍是一个Future,所以我们可以在then方法后放置多个then,放置多个then时只有当前面的then函数执行完毕后后面的then才会回调到,也就是会顺序执行。

Future.delayed(Duration(milliseconds: 1000), () {
  print("111");
}).then((value){
  print("222");
}).then((value){
  print("333");
});

控制台:
111
222
333

Future.catchError()

此方法用来捕获Future执行异常错误,需要注意的是,如果catchError写在then方法前面执行then方法是可以调用到的,如果写在then方法后面则then方法中的代码不会执行,所以在使用时一定要注意逻辑顺序。

2021-10-08 18.12.52.gif

2021-10-08 18.13.10.gif

Future.whenComplete()

此方法表示Future异步执行完毕后一定会调用的方法,不管是否有异常情况。

Future.delayed(Duration(milliseconds: 100), () {
  throw NullThrownError();
}).then((value){
  print("222");
}).catchError((error){
  print(error);
}).whenComplete((){
  print("333");
});

控制台:
程序开始
Throw of null.
333

进阶方法

Future.foreach()

根据传入的集合,从中取值并创建集合长度个数的Future并顺序执行。

Future.forEach([1,2,3], (element) => print(element));
控制台:
程序开始
1
2
3

Future.timeout()

如果你希望一个异步任务在固定时间内回调,没有回调回来就出现错误,那么使用这个方法可以满足要求。

Future.delayed(Duration(milliseconds: 2000), () {
  print("111");
}).then((value){
  print("222");
}).timeout(Duration(milliseconds: 1000));
控制台:
程序开始
Unhandled exception:
TimeoutException after 0:00:01.000000: Future not completed

Future.wait()

传入多个Future的集合,当这些任务全部执行完成时Future.wait将所有执行结果统一返回.非常适合在某些场景下,一个数据的显示依赖于多个网络接口返回时就可以使用此方法。

Future future1 = Future.value(1);
Future future2 = Future.value(2);
Future future3 = Future.value(3);

Future.wait([future1,future2,future3]).then((value) =>
    print("全部执行完毕${value[0]} ${value[1]} ${value[2]}"));
控制台:
程序开始
全部执行完毕1 2 3

Future.any()

传入一个集合,从中取值并创建集合长度个数的Future并顺序执行并返回第一个执行完成的结果。

Future future1 = Future.value(1);
Future future2 = Future.value(2);
Future future3 = Future.value(3);

Future.any([future1,future2,future3]).then((value) =>
    print("第一个执行完毕的:$value")
);
控制台:
程序开始
第一个执行完毕的:1

以上就是自己对Future的总结,如有错误,还望指出