一、认识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将报告为未处理的错误。

Flutter异步Future的更多相关文章

  1. Flutter 异步Future与FutureBuilder实用技巧

    什么是Future? Future表示在接下来的某个时间的值或错误,借助Future我们可以在Flutter实现异步操作.它类似于ES6中的Promise,提供then和catchError的链式调用 ...

  2. Netty异步Future源码解读

    本文地址: https://juejin.im/post/5df771ee6fb9a0161d743069 说在前面 本文的 Netty源码使用的是 4.1.31.Final 版本,不同版本会有一些差 ...

  3. flutter 异步async、await和Future的使用技巧

    由于前面的HTTP请求用到了异步操作,不少小伙伴都被这个问题折了下腰,今天总结分享下实战成果.Dart是一个单线程的语言,遇到有延迟的运算(比如IO操作.延时执行)时,线程中按顺序执行的运算就会阻塞, ...

  4. Flutter异步与线程详解

    一:前言 - 关于多线程与异步 关于 Dart,我相信大家都知道Dart是一门单线程语言,这里说的单线程并不是说Dart没有或着不能使用多线程,而是Dart的所有API默认情况下都是单线程的.但大家也 ...

  5. Flutter异步编程 http网络请求数据

    import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as ht ...

  6. Flutter main future mirotask 的执行顺序

    下面这段代码的输出是什么? import 'dart:async'; main() { print('main #1 of 2'); scheduleMicrotask(() => print( ...

  7. 26Flutter 日期 和时间戳/格式化日期库/flutter异步/ 官方自带日期组件showDatePicker、时间组件showTimePicker以及国际化

    /* 一.Flutter日期和时间戳 日期转换成时间戳 var now=newDateTime.now(); print(now.millisecondsSinceEpoch); //单位毫秒,13位 ...

  8. Rust异步之Future

    对异步的学习,我们先从Future开始,学习异步的实现原理.等理解了异步是怎么实现的后,再学习Rust异步编程涉及的2个库(futures.tokio)的时候就容易理解多了. Future rust中 ...

  9. [Flutter] 一些面试可能会问基础知识

    1. Flutter 是什么? Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter正在被 ...

随机推荐

  1. R语言-程序执行时间

    我们往往对自己编写程序的运行效率十分关心,需要查看程序的执行时间. 在R中,获得时间的函数有不少,比如system.time().proc.time()等. 个人使用较多的是proc.time() & ...

  2. Struts2基本原理【转】

    阐述struts2的执行流程. Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher.业务控制器Action和用户实现的企业业务逻辑组件. 核心控制器FilterD ...

  3. 【51nod 2026】Gcd and Lcm

    题目 已知 \(f(x)=\sum_{d|x}μ(d)∗d\) 现在请求出下面式子的值 \(\sum_{i=1}^{n}\sum_{j=1}^{n}f(gcd(i,j))∗f(lcm(i,j))\) ...

  4. Documents.Open返回值为null

    上个月出现的一个问题,将解决方法记录一下~ [问题]无法通过SAP系统外部OS命令(SM69)执行OS服务器上Powershell文件对Office文档的相关命令操作(打开文档等命令). [现象]执行 ...

  5. Linux设置程序开机自启动,系统命令chkconfig及linux /etc/rc.d/目录的详解

     整理了linux下程序开启几种方式,转载相关博客做统一记录 <linux程序设置开机自启动>转载自:https://www.cnblogs.com/flcz/p/7691532.html ...

  6. JAVA如何跳出多层循环

    1. break.continue.return 的区别: break默认是跳出最里层的循环,也就是break所在的最近的那层循环 continue是终止本次循环,继续下次循环 return 结束当前 ...

  7. [游记]CSP2019-S

    Day 1 开局看到T1格雷码,哇塞这不是初赛原题???10分钟高精打完离场. T2是个什么题目,看起来不难,15分钟码完,调了5分钟,过了样例2 欸,为什么样例3过不掉?仔细一看发现爆栈了,一慌忘记 ...

  8. Linux iptables 防火墙设置

    1.查看防火墙iptables -L -niptablesb -L -n --line-number  显示规则行号看到 INPUT ACCEPT, FORWARD ACCEPT , OUTPUT A ...

  9. Linux 环境安装运行Sqlmap

    1.官网下载 .tar.gz 文件   官网地址:http://sqlmap.org/ 2.登录访问linux环境,将压缩包放入/usr/local 路径. 3.在该路径下通过 tar -xzvf f ...

  10. spring aop 实现controller 日志

    @Aspect @Component @Slf4j public class ControllerAspact { @Pointcut("execution(public * com.exa ...