【Flutter 專題】91 圖解 Dart 單執行緒實現非同步處理之 Future (二)

阿策小和尚發表於2021-07-18

      小菜前幾天剛學習了 Future 實現非同步操作的部分方法,主要包括構造方法和常用的靜態方法;小菜今天繼續學習 Future 其他知識和 async-await 方式實現非同步操作;

Future 巢狀

      小菜在上篇部落格中未做 Future 巢狀嘗試,有很多場景需要多個非同步處理,且每個非同步都需要上個非同步返回的結果 then() 之後才可以繼續,此時可以用 Future 巢狀方式;但如果潛套方法較多可能會對今後的程式碼維護造成一定影響,即常說到的 Callback hell

_futureMoreThen() {
  Future.delayed(Duration(seconds: 3), () {
    return 'Future.delayed 3s!';
  }).then((val) {
    print('Future -> then(1) -> $val');
    return 'then 1';
  }).then((val) {
    print('Future -> then(2) -> $val');
    return 'then 2';
  }).then((val) {
    print('Future -> then(3) -> $val');
    return 'then 3';
  }).whenComplete(() => print('Future whenComplete!'));
}
複製程式碼

_futureMoreThen02() {
  Future.delayed(Duration(seconds: 3), () {
    return 'Future.delayed 3s!';
  }).then((val) {
    print('Future -> then(1) -> $val');
    return Future.delayed(Duration(seconds: 2), () {
      return 'Future.delayed 2s!';
    }).then((val) {
      print('Future -> then(2) -> $val');
      return Future.delayed(Duration(seconds: 2), () {
        return 'Future.delayed 2s!';
      }).then((val) {
        print('Future -> then(3) -> $val');
      });
    });
  }).whenComplete(() => print('Future whenComplete!'));
}
複製程式碼

async-await

      Future 也可以通過 async-await 實現非同步操作;其使用場景通常是在多個 Future 串聯起來,多層級巢狀而導致的 Callback hell,使用 async-await 實現非同步;

async

      async 用來修飾的非同步方法最終將返回值封裝成 Future 物件;

await

      await 會把自動把該方法進入阻塞狀態,一直待任務執行完成並返回對應值;

案例嘗試

      小菜先嚐試了基本的 async-await 用法;

  1. 小菜未採用 asyncawait 關鍵詞,此時 Future.delayed() 返回的是一個 Future 物件,不能同步的獲取返回資料;
print(_function01());

_function01() {
  var result = Future.delayed(Duration(seconds: 3), () {
    return 'Future.delayed 3s!';
  }).then((val) => print('Function01 -> then() -> $val'))
    .whenComplete(() => print('Function01 -> whenComplete!'));
  return 'Function01 -> $result';
}
複製程式碼

  1. 小菜僅新增了 async 關鍵詞,將該方法修飾為非同步方法,依舊不能直接獲取返回資料
print(_function01());

Future<String> _function02() async {
  var result = Future.delayed(Duration(seconds: 3), () {
    return 'Future.delayed 3s!';
  }).then((val) => print('Function02 -> then() -> $val'))
    .whenComplete(() => print('Function02 -> whenComplete!'));
  return 'Function02 -> $result';
}
複製程式碼

  1. 小菜新增了 asyncawait 兩個關鍵詞,編譯器最終會將其轉化為一個 Promise(Future) 的呼叫鏈,可以待非同步完成之後獲取返回結果;此時 Future 不能設定 then() 回撥方法等;
print(await _function03());

_function03() async {
  var result = await Future.delayed(Duration(seconds: 3), () {
    return 'Future.delayed 3s!';
  });
  return 'Function03 -> $result';
}
複製程式碼

  1. 小菜嘗試只用 await,此時提示 The await expression can only used in an async functionawait 只能用在 async 方法內部;
  2. 採用 async-await 方式時,對於異常的捕獲,可以通過 Future.catchError() 來處理,還可以採用最常用的 try-catch-finally 方式,小菜簡單理解對應 then()-catchError()-whenComplete()
await _function04();

_function04(index) async {
  switch (index) {
    case 1:
      await Future.error(ArgumentError.notNull('Input')).catchError((val) => print('Function04 -> $val'));
      break;
    case 2:
      try {
        await Future.error(ArgumentError.notNull('Input'));
      } catch (e) {
        print('Function04 -> catch -> $e');
      } finally {
        print('Function04 -> Finally!');
      }
      break;
  }
}
複製程式碼

  1. 針對多個 Future 巢狀導致的 Callback hellasync-await 處理方式要簡潔一些;
await _functionThen();

_functionThen() async {
  await _itemThen01();
}

_itemThen01() async {
  await Future.delayed(Duration(seconds: 3), () {
    print('Future -> then(1) -> Future.delayed 3s!');
    return _itemThen02();
  });
}

_itemThen02() async {
  await Future.delayed(Duration(seconds: 2), () {
    print('Future -> then(2) -> Future.delayed 2s!');
    return _itemThen03();
  });
}

_itemThen03() async {
  await Future.delayed(Duration(seconds: 2), () {
    print('Future -> then(3) -> Future.delayed 2s!');
    return 'Future.delayed 2s!';
  }).whenComplete(() => print('Future whenComplete!'));
}
複製程式碼

小擴充套件

      小菜在嘗試 async-await 時還遇到 async*,小菜在 bloc 狀態管理時使用時都是 async* 和 Stream,小菜簡單瞭解一下相關差異;

async*

      async* 也可以用於非同步,方法前使用 async* 關鍵字可以將該方法標記為非同步生成器,返回的是一個 Stream 物件,使用 yield 語句來傳遞值;

      對於 Stream 的使用,小菜之前有基本的瞭解,一般通過 skin 新增資料,通過 listen 進行資料監聽;

      yield 關鍵字會向 async* 宣告的一步生成器的輸出流新增一個值,有點類似 return,但不會終止函式;

 _function06() async* {
  for (int i = 1; i <= 10; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}
複製程式碼
await for (int num in _function06()) {
  print('current num = $num');
}
複製程式碼

_function06().listen((num) => print('current num = $num'));
複製程式碼


      Dart async-await 案例嘗試


      小菜對 Dart 非同步的認知還不完全,接下來會繼續嘗試 isolate 以及 EventLoop 執行順序等;如有錯誤和遺漏請多多指導!

來源: 阿策小和尚

相關文章