43

Flutter 异步编程:Futures

 4 years ago
source link: https://mp.weixin.qq.com/s/PVTNgDItvGYI3lH0lfWZoA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

QfqYzqf.png!web

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数据。

e2Mz2iN.gif

有几个术语需要理解下:

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

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

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

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

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或者三方库已经封装好了,直接使用,比如

<span>final future1 = http.get(&quot;httts://www.google.com&quot;);</span>

<span>final future2 = SharedPreferences.getInstance();</span>

<span>&middot;&middot;&middot;</span>

2. Future类的工厂方法

eyQbmau.png!web

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

<span>/// factory Future(FutureOr&lt;T&gt; computation()) </span>

<span>/// 创建一个包含调用computation结果的future。</span>

<span>final myFuture = Future(() {</span>

<span> return &quot;HelloFlutter!&quot;;</span>

<span>});</span>

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

<span>/// factory Future.microtask(FutureOr&lt;T&gt; computation())</span>

<span>/// 创建一个future,scheduleMicrotask将computation放到微任务调用处理,然后返回结果。</span>

<span>final myFuture = Future.microtask(() {</span>

<span> return &quot;HelloFlutter!&quot;;</span>

<span>});</span>

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

77ziiif.jpg!web

<span>/// factory Future.sync(FutureOr&lt;T&gt; computation()) </span>

<span>/// 创建一个future,包含立即调用computation的结果。</span>

<span>/// 如果结果类型为Future&lt;T&gt;,则直接返回</span>

<span>/// 如果不为Future&lt;T&gt;,则会创建并返回一个已经完成的future,值value为result</span>

<span>final myFuture = Future.sync(() {</span>

<span> return &quot;HelloFlutter&quot;;</span>

<span>});</span>

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

<span>/// factory Future.value([FutureOr&lt;T&gt; value])</span>

<span>/// 创建一个值为value的成功态future。</span>

<span>final myFuture = Future.value(&quot;HelloFlutter!&quot;);</span>

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

<span>/// factory Future.error(Object error, [StackTrace stackTrace])</span>

<span>/// 创建一个error的失败态future,可选参数为堆栈信息</span>

<span>final myFuture = Future.error(Exception());</span>

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

<span>/// factory Future.delayed(Duration duration, [FutureOr&lt;T&gt; computation()])</span>

<span>/// 创建一个延时执行computation的future</span>

<span>final myFuture = Future.delayed(Duration(seconds: 2), () =&gt; &quot;HelloFlutter!&quot;);</span>

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 的机制,才能在复杂的业务场景中或者构建基础架构时游刃有余,立于不败之地。

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

VNjUNfQ.jpg!web

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 等来链式操作。比如

<span> Future doStuff(){</span>

<span> return someAsyncOperation().then((result) {</span>

<span> return someOtherAsyncOperation(result);</span>

<span> });</span>

<span> }</span>

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

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

<span>Future queryName(int id) {</span>

<span> // 创建一个completer</span>

<span> var completer = new Completer();</span>

<span> </span>

<span> // 查询数据库,然后根据成功或者失败执行相应的callback回调,这个过程是异步的</span>

<span> database.query(&quot;select name from user where id = $id&quot;, (results) {</span>

<span> completer.complete(results);</span>

<span> }, (error) {</span>

<span> completer.completeError(error);</span>

<span> });</span>

<span> </span>

<span> // return 在query结果出来之前就会被执行</span>

<span> return completer.future;</span>

<span>}</span>

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

1)await方式

<span>void runFuturesDemo2() async {</span>

<span> try {</span>

<span> final name = await queryName(100); </span>

<span> print(&quot;id 100 user name is $name&quot;);</span>

<span> } catch (e) {</span>

<span> print(e.toString());</span>

<span> } finally {</span>

<span> print(&quot;finally finished.&quot;);</span>

<span> }</span>

<span>}</span>

2)then方式

<span>void runFuturesDemo2() {</span>

<span> queryName(100).then((name) {</span>

<span> print(&quot;id 100 user name is $name&quot;);</span>

<span> }, onError: (e) {</span>

<span> print(e.toString());</span>

<span> }).whenComplete(() {</span>

<span> print(&quot;finally finished.&quot;);</span>

<span> });</span>

<span>}</span>

类似自定义 Promise 我们看下:

Q3mMRf2.png!web

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

自定义 Future 如下:

EZfqUve.png!web

推荐读者阅读  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  源码


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK