Flutter进阶篇(4)-- Flutter的Future异步详解一、认识Future二、创建多个Future的执行步骤三、then函数嵌套使用的执行步骤四、综合示例五、我们来看看Future的源码
https://cloud.tencent.com/developer/article/1375846
本文首发在公众号Flutter那些事
,欢迎大家多多关注。
说明:本文中的所有函数的引用在main
函数中:
main() async {testFuture();testFuture2();testFutureCreate1();testFutureCreate2();testFutureCreate3();testThen1();testThen2();testThen3();testThen4();testAll(); }
一、认识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
和我们创建Future对象的先后顺序完全一致。
2 1 3
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
调用中同时处理value
和error
。
请注意,在添加监听器(listener)之前,future
不会延迟报告错误。如果第一个then
或catchError
调用在future
完成后发生error
,那么error
将报告为未处理的错误。
Flutter进阶篇(4)-- Flutter的Future异步详解一、认识Future二、创建多个Future的执行步骤三、then函数嵌套使用的执行步骤四、综合示例五、我们来看看Future的源码相关推荐
- (进阶篇)Cookie与 Session使用详解
1.Cookie和Session简介与区别 在非常多时候,我们需要跟踪浏览者在整个网站的活动,对他们身份进行自动或半自动的识别(也就是平时常说的网站登陆之类的功能),这时候,我们常采用Cookie与 ...
- Android AOSP基础(五)不会调试系统源码,还搞什么Android?
本文首发于微信公众号「刘望舒」 关联系列 Android AOSP基础系列 Android系统启动系列 应用进程启动系列 Android深入四大组件系列 Android深入理解Context系列 An ...
- spring源码分析第五天------springAOP核心原理及源码分析
spring源码分析第五天------springAOP核心原理及源码分析 1. 面向切面编程.可以通过预 编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术 切面(A ...
- 分布式定时任务—xxl-job学习(四)——调度中心web页面端api调用源码分析
分布式定时任务-xxl-job学习(四)--调度中心web页面端api调用源码分析 前言 一.controller目录下非controller类 1.1 PermissionLimit自定义注解 1. ...
- Future 用法详解
Future 用法详解 前言 其他知识点 Java 多线程基础 深入理解aqs ReentrantLock用法详解 深入理解信号量Semaphore 深入理解并发三大特性 并发编程之深入理解CAS 深 ...
- 曝肝三天,两千行Python代码,制作B站视频下载工具(附源码)
曝肝三天,两千行Python代码,制作B站视频下载工具(附源码) 文章目录 一.准备工作 二.预览 1.启动 2.解析 3.下载中 4.下载完成 5.结果 三.设计流程 1.bilibili_vide ...
- java并发编程Future类详解
作用和举例 future类的作用就是为了调用其他线程完成好后的结果,再返回到当前线程中,如上图举例: 小王自己是主线程,叫外卖等于使用future类,叫好外卖后小王就接着干自己的事去了,当外卖到了的时 ...
- JavaScript实现闭式函数计算特定位置的斐波那契数fibonacciNthClosedForm算法(附完整源码)
JavaScript实现闭式函数计算特定位置的斐波那契数fibonacciNthClosedForm算法(附完整源码) fibonacciNthClosedForm.js完整源代码 fibonacci ...
- [Python从零到壹] 四十五.图像增强及运算篇之图像灰度非线性变换详解
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
最新文章
- 思维dp ---- CF41D Pawn [可达状态统计dp]
- Ubuntu编译开源卡丁车(supertuxkart)项目
- 在Linux下安装和使用MySQL(转)
- starting Tomcat v8.5 at localhost has encountered a problem
- 理解CSRF(跨站请求伪造)
- 组素数 蓝桥填空题2013省赛
- 操纵浏览器的历史记录
- 第一章 Visual Basic入门
- 二蛋和培训机构斗智斗勇的血泪史
- 输入输出练习 python
- opencv实现阈值分割算法和分水岭算法
- 密码学之BGN同态加密算法
- 仿微信表情输入键盘(支持 Gif 表情图文混排 )
- 华三(h3c)交换机操作命令详解vlan切换
- 成为游戏开发程序员,要学些什么
- 2022工作中遇到的问题四
- 平克四部曲之《白板》
- 经济基础知识(中级)【10】
- 高端时尚飞行模拟体验为展会聚拢人气
- OSError: cannot load library ‘C:\...\site-packages\_soundfile_data\libsndfile64bit.dll‘: error 0x7e