专栏首页小菜与老鸟Flutter异步编程

Flutter异步编程

Flutter异步编程-Futures

本文大纲

1. 什么是Future?

2. 如何创建Future实例?

3. 一个令人迷惑的例子

4. 参考和更多阅读

1. 什么是Future?

本文小菜带大家认识和理解下 Futures 的用法以及原理。

经常听说 future,或者从其他语言见到类似的说法如 javascript 的 Promise。那么究竟什么是 future?

A future (lower case “f”) is an instance of the Future (capitalized “F”) class. A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed. ​​https://dart.dev/codelabs/async-await

future 是 Future 类的实例,表示一个异步操作的结果,这个结果会有两种状态:未完成和已完成

我们可以将future理解成一个未知的盒子,盒子里包裹着一个value值,类型为T,这个盒子会被投递到你的手中,盒子没到达你的手中时处于未完成状态,到达你的手中后,打开盒子,可能是成功返回的data数据(也包括没有返回值void),也可能是失败返回的error数据。

有几个术语需要理解下:

- 同步操作:同步操作会阻塞后面其他的操作直至完成

- 同步函数:同步函数内部一定都是同步操作,顺序完成

- 异步操作:异步操作允许在其完成之前进行其他操作(执行其他代码)

- 异步函数:异步函数内部至少有一个异步操作,允许有同步操作和同步函数存在。

1.1 什么是未完成?

当我们调用一个异步函数,异步函数会返回一个未完成的 future 实例。这个 future 会等待异步函数的操作完成或者失败抛出错误异常。

1.2 什么是已完成?

当异步操作成功,future便会以操作的结果结束,否则以错误结束。

我们常常见到异步函数返回值类型为 Future<T>。Future<T>表示将来某个时间点异步操作执行成功或者失败的结果,结果类型为T。如果无返回结果,使用Future<void>表示。

所以将已完成再拆分,future可以认为有三种状态:

- Uncompleted 未完成 (类似Promise的pending)

- Completed with data 成功,返回data数据(类似Promise的fulfilled)

- Complted with error 失败,返回error数据(类似Promise的rejected)

2. 如何创建Future实例?

1. 一些已有的封装api

一些常用的api或者三方库已经封装好了,直接使用,比如

final future1 = http.get("httts://www.google.com");
final future2 = SharedPreferences.getInstance();
···

2. Future类的工厂方法

A) factory Future(FutureOr<T> computation())

/// factory Future(FutureOr<T> computation()) 
/// 创建一个包含调用computation结果的future。
final myFuture = Future(() {
    return "HelloFlutter!";
});

B) factory Future.microtask(FutureOr<T> computation())

/// factory Future.microtask(FutureOr<T> computation())
/// 创建一个future,scheduleMicrotask将computation放到微任务调用处理,然后返回结果。
final myFuture = Future.microtask(() {
    return "HelloFlutter!";
});

C) factory Future.sync(FutureOr<T> computation())

/// factory Future.sync(FutureOr<T> computation())  
/// 创建一个future,包含立即调用computation的结果。
/// 如果结果类型为Future<T>,则直接返回
/// 如果不为Future<T>,则会创建并返回一个已经完成的future,值value为result
final myFuture = Future.sync(() {
    return "HelloFlutter";
});

D) factory Future.value([FutureOr<T> value])

/// factory Future.value([FutureOr<T> value])
/// 创建一个值为value的成功态future。
final myFuture = Future.value("HelloFlutter!");

E) factory Future.error(Object error, [StackTrace stackTrace])

/// factory Future.error(Object error, [StackTrace stackTrace])
/// 创建一个error的失败态future,可选参数为堆栈信息
final myFuture = Future.error(Exception());

F) factory Future.delayed(Duration duration, [FutureOr<T> computation()])

/// factory Future.delayed(Duration duration, [FutureOr<T> computation()])
/// 创建一个延时执行computation的future
final myFuture = Future.delayed(Duration(seconds: 2), () => "HelloFlutter!");

3. 一个令人迷惑的例子

// main.dart
void main() {
  runFuturesDemo();
}
// futures_demo.dart
void runFuturesDemo() async {
  print("runFuturesDemo start...");

  // future1 ------------------------------------
  Future future1 = new Future(() => null);
  future1.then((_) {
    print("future1 then 1");
  }).catchError((e) {
    print("future1 catchError");
  }).whenComplete(() {
    print("future1 whenComplete");
  });

  // future2 ------------------------------------
  Future future2 = Future(() {
    print("future2 init");
  });

  future2.then((_) {
    print("future2 then");
    future1.then((_) {
      print("future1 then3");
    });
  }).catchError((e) {
    print("future2 catchError");
  }).whenComplete(() {
    print("future2 whenComplete");
  });

  future1.then((_) {
    print("future1 then2");
  });

  // future3 ------------------------------------
  Future future3 = Future.microtask(() {
    print("future3 init");
  });

  // future4 ------------------------------------
  Future future4 = Future.sync(() {
    print("future4 init");
  });

  // future5 ------------------------------------
  Future future5 = Future(() {
    print("future5 init");
  });

  // future6 ------------------------------------
  Future future6 = Future.delayed(Duration(seconds: 3), () {
    print("future6 init");
  });

  print("runFuturesDemo end...");
}

聪明的你,思考下,打印顺序是什么呢?

深刻理解 futures 的机制,才能在复杂的业务场景中或者构建基础架构时游刃有余,立于不败之地。

下面是正确的输出,符合你的预期吗?如果不符合的话,是哪里理解不对呢?

demo地址:https://github.com/dabing1022/flutter_async_programming

事实a)执行 main 函数,在 main 里面会往 microtask queue 和 event queue 中添加任务和事件, 包括注册一些回调,结束后,开启event loop

事实b)事件循环中 microtask queue 优先级 > event queue 优先级

事实c)从上面工厂方法 Future.sync 源码截图中可以看到,先同步执行computation(于是打印"future4 init"),根据 computation() 返回值视情况进行 Future 封箱(如果 compuation() 返回值为 Future<T>,直接返回,如果不是,则使用 _Future.value 将结果封箱)。

事实d)为什么 7 排在 8 前面?

我们看源码 then 的注释:

Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
   * Register callbacks to be called when this future completes.
   *
   * When this future completes with a value,
   * the [onValue] callback will be called with that value.
   * If this future is already completed, the callback will not be called
   * immediately, but will be scheduled in a later microtask.

因为 future1 已经 completed 了,所以 future1 在7这个位置再次用 then 注册的 callback 回调会被放在 microtask 中执行。microtask的优先级高,所以 7 会在 8 前面打印。

事实e)为什么 11 排在 9 后面而不是 7 后面?

future2 在 event queue 事件队列中排在了 future1 后面,future2 init 执行完毕后,交给 then 的回调处理,此时位置 11 处,即 future1 使用 then 注册的 callback 在 future2 的 then 的 callback 里面,所以会处在 9 后面而不是 7 后面。

flutter: runFuturesDemo start...
flutter: future4 init
flutter: runFuturesDemo end...
flutter: future3 init
flutter: future1 then 1
flutter: future1 whenComplete
flutter: future1 then2
flutter: future2 init
flutter: future2 then
flutter: future2 whenComplete
flutter: future1 then3
flutter: future5 init
flutter: future6 init

4. 如何自定义Future?

无论是在做基础架构设计还是业务设计中,常常会需要自定义 Future。我们如何自定义 Future,其实可以参考源码的写法。

关键字 Completer

Completer是一种可以生成以value或者error为结果的Future对象的一种方式。

大部分时候,我们创建future,可以使用上面提到的工厂方法来创建,比如

new Future(() { doSomething(); return result; });

如果future表示一个异步操作序列的结果,那么可以使用 Future.then 等来链式操作。比如

 Future doStuff(){
    return someAsyncOperation().then((result) {
        return someOtherAsyncOperation(result);
    });
 }

但是如果我们想自定义一个 Future,比如我们需要将基于 callback 回调的一个 api 转化为基于 future 设计的流程,如何做呢?

这里我举个例子,我们在数据库里根据 id 查询名字:

Future queryName(int id) {
  // 创建一个completer
  var completer = new Completer();
  
  // 查询数据库,然后根据成功或者失败执行相应的callback回调,这个过程是异步的
  database.query("select name from user where id = $id", (results) {
    completer.complete(results);
  }, (error) {
    completer.completeError(error);
  });
  
  // return 在query结果出来之前就会被执行
  return completer.future;
}

后面我们如何使用 queryName 呢?常见两种方式来"打开箱子"await 或者 then

1)await方式

void runFuturesDemo2() async {
    try {
        final name = await queryName(100);  
        print("id 100 user name is $name");
    } catch (e) {
        print(e.toString());
    } finally {
        print("finally finished.");
    }
}

2)then方式

void runFuturesDemo2() {
  queryName(100).then((name) {
    print("id 100 user name is $name");
  }, onError: (e) {
    print(e.toString());
  }).whenComplete(() {
    print("finally finished.");
  });
}

类似自定义 Promise 我们看下:

Promise使用 resolve 和 reject 来执行成功或者异常,data 或者 error 在 then 的注册回调里面被使用。

自定义 Future 如下:

推荐读者阅读 future.dart future_impl.dart 源码。

5. 参考和更多阅读

- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

- https://dart.dev/codelabs/async-await

- 阅读 future.dart 和 future_impl.dart 源码

本文分享自微信公众号 - 小菜与老鸟(xiaocai_laoniao),作者:ChildhoodAndy

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iTerm2使用小技巧-密码管理器

    老鸟好奇的问小菜:“小菜,你这看来终端用的是iTerm2呀,呦,主题用的 Oh My Zsh 吧。Cool~”。

    ChildhoodAndy
  • Dart中的异步操作

    在前面的文章中我们很多次提到了Future这个东西,这个单词翻译过来的意思是‘未来’的意思。在flutter中它表示一个未来某些时候返回数据的一个对象。

    flyou
  • Flutter必备语言Dart教程04 - 异步,库

    现在我们来看看如何在Dart中处理异步代码。使用Flutter时,会执行各种操作,例如网络调用和数据库访问,这些操作都应该异步执行。

    前端知否
  • 零成本异步 I/O (下)

    这个非常出色的基于轮询的新方案——我们编写了这个模型,我归功于 Alex 和 Aaron Turon,是他们提出了这个想法——不是由 Future 来调度回调函...

    MikeLoveRust
  • netty案例,netty4.1源码分析篇六《Netty异步架构监听类Promise源码分析》

    他们的含义都是对未来即将要发生的事情做相应的处理,这也是在异步编程中非常常见的类名。

    小傅哥
  • # Event loop

    ​ dart是一种单线程语言,异步模型主要是通过事件轮询(event loop)来实现,另外也提供了更高级的Isolate来支持多线程,通常用于计算比较耗时的操...

    用户1175783
  • Flutter异步编程Future与FutureBuilder的实用技巧

    在这篇文章中,将向大家分享异步编程Future与FutureBuilder的一些实用知识和技巧,首先会带着大家认识什么是Future?、Future的常见用法?...

    CrazyCodeBoy
  • Java多线程编程-(19)-多线程异步调用之Future模式

    在《Java多线程编程-(8)-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier》 这一篇中,我们使用线程计数器的方式实现了...

    Java后端技术
  • 【Flutter 专题】90 图解 Dart 单线程实现异步处理之 Future (一)

    和尚尝试过 Future 和 Stream 实现 Dart 异步处理,但仅限于基本的使用,网上有很多相关的资料,和尚仅从初识者的角度学习了解 Dart 的实现的...

    阿策
  • 深圳scala-meetup-20180902(1)- Monadic 编程风格

    刚完成了9月份深圳scala-meetup,趁刮台风有空,把我在meetup里的分享在这里发表一下。我这次的分享主要分三个主题:“Monadic编程风格“、...

    用户1150956

扫码关注云+社区

领取腾讯云代金券