Day09 | Dart 非同步 - Future

昨天介绍了在Dart中非同步的基本概念,今天就要来讲到如何简单的控制非同步操作。

Future

Future 可以想像成一个盒子一样,它将 「一个值装起来直到未来某个时间点才会打开」

直接看code,我们宣告了一个function他会回传一个 Future<String> ,同时我也会看到 Future 最基本的 constructor Future(FutureOr<T> computation())

所以这边我们先传入一个会回传String的 Function

Future<String> fetchData() => Future(
      () => 'Data',
    );

然後到main里使用:

final data = fetchData();
print(data);

会发现输出不是 Data 而是 Instance of 'Future<String>' 为什麽呢?就像前面所说的Future 可以想像成一个盒子一样,所以我们必须将它打开才能取出它的值。

而打开这个盒子的其中一种方法就是用 then

fetchData().then((value) => print(value));
// Data

.then 会回传一个callback 然後我们就可以用 (value) => print(value) 这种形式来使用它,那如果我的非同步是有一连串的顺序呢?

假设我想要非同步的取得一个资料後,再经过三秒後才输出的话

一样先定义两个回传 Future<String> 的 function

Future<String> outputAfter3s(String data) => Future.delayed(
      Duration(seconds: 3),
      () => data,
    );

Future<String> fetchData() => Future(
      () => 'data',
    );

在使用上就直接插入一个 .then 就是这麽简单

fetchData()
      .then(
        (value) => outputAfter3s(value),
      )
      .then((value) => print(value))
     

整个流程大概如下:

outputAfter3s 回传一个 Future 後,一样是一个FutureOr<T> computation() 所以可以继续往下接.then ,这个FutureOr 意思是可能T 或者 Future<T>

所以其实也可以直接传一个value下去

fetchData()
      .then(
        (value) => outputAfter3s(value),
      )
      .then((value) => value)
      .then((value) => print(value.length))
// 4

错误处理

Future 另外一个好处是可以让我们更方便的catch error

Future throwError() => Future(
      () => throw 'error 123456',
  );
// 省略其他code ...

fetchData()
			.then((value) => throwError())
      .then(
        (value) => outputAfter3s(value),
      )
      .then((value) => value)
      .then((value) => print(value.length))
      .catchError(
        (err) => print('catch error: $err'),
      );

我们这边先直接宣告一个一定会throw error的function,当然在实务上常见的可能会是http client发生一些错误才会throw error。

然後我们利用 catchError 来做错误处理,使用方式也很简单,它一样会回传一个 callback 里面可以取得这次非同步中throw error。

所以会有以下输出

catch error: error 123456

完成状态

如果我有一些操作是想要整个Future chain 都结束後且 「无论失败或成功」 都要执行的话那我该如何写?

fetchData()
      .then((value) => throwError())
      .then(
        (value) => outputAfter3s(value),
      )
      .then((value) => value)
      .then((value) => print(value.length))
      .catchError(
        (err) => print('catch error: $err'),
      )
			.whenComplete(() => print('completed'));

可以使用 whenComplete 这个api 来达成而且他是 「无论失败或成功」 只要这个 Future有了结果回传都会执行。

所以输出会是这样

catch error: error 123456
completed

如果没有throw error:

4
completed

所以我们现在可以得知future有三种状态 「成功」、「失败」、「未完成」 ,但为什麽没有讲解到 「未完成」 (pending)时候的控制呢?主要是因为这件事情有了UI才会比较需要呈现。所以等之後进入到flutter的文章时再来慢慢介绍要如何实作分别呈现 「成功」、「失败」、「未完成」 这三种UI。


这次讲了算是相当常见的 Future ,但其实我们在绝大多数的场景下很少自己建立Future ,大多数时候都是第三方服务会回传Future 来让我们做控制像是http request 之类的。

但你可能会想问难道我只能从 then 里面才能将值取出来吗?如果我有将Future 里的值取出来放到另外一个变数该如何做?其实如果有state存在的话,是可以在Future chain里去将state改变。但大多数的做法应该都会是利用 async/await 来达成这件事情,就等到明天再来好好说明async/await 了。

今天的程序码:

https://github.com/zxc469469/dart-playground/tree/Day09/future

参考资料

https://www.youtube.com/watch?v=OTS-ap9_aXc


<<:  07 - Metrics - 观察系统的健康指标 (1/6) - Metrics 与 Metricbeat 的基本介绍

>>:  Day08:08 - User服务(3) - 後端 - JWT token、修改个人资料

Day17:图形搜寻-贝尔曼-福特演算法(Bellman-Ford algorithm)

最短路径演算法 最短路径是在赋予edges权重的「加权图形」里,指定「起点」和「终点」,求出起点到终...

用React刻自己的投资Dashboard Day15 - 投资Dashboard 2.0版 Wireframe

有了总体经济的图表之後,接下来就要来制作各国股市的资讯站,笔者最常看的就是台股的资讯,其次则是美股、...

成员 3 人:别让人落单,就成功一半

「三是一个质数,是一个特别的存在。」 「三角形是最坚固的形状,最强韧的组合。」 三个人的团队,是最适...

成为工具人应有的工具包-06 WirelessKeyView

WirelessKeyView 今天来认识 WirelessKeyView这个酷东西! (还有其他密...

Veeam Backup专业级备份软件从入门到实战_01

Veeam Backup专业级备份软件从入门到实战_01 课程大纲: 1.Veeam公司介绍 2.V...