iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Modern Web

Flutter web 的奇妙冒險系列 第 9

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


上一篇
Day08 | Dart 中的非同步 - Isolate、Event loops
下一篇
Day10 | Dart 非同步 - async/awiat
系列文
Flutter web 的奇妙冒險30

尚未有邦友留言

立即登入留言