Dart 異步編程注意事項(xiàng)

前言

Dart 語言提供了多種異步編程方式,比如 Future,比如async / await,再比如 Stream。如何更好地進(jìn)行異步編程,我們來看看官方的指引。

相比 Future,優(yōu)先使用 async / await

異步代碼的可讀性差和難于調(diào)試是臭名昭著的,即便是使用 Future 這樣比較好的抽象方式也是一樣。 async / await 語法改善了可讀性,并且可以在所有 Dart 的控制流的異步代碼中使用。下面是一個(gè)典型的例子。通過 async / await 語法糖,代碼邏輯非常清晰易懂。

// 正確示例
Future<int> countActivePlayers(String teamName) async {
  try {
    var team = await downloadTeam(teamName);
    if (team == null) return 0;

    var players = await team.roster;
    return players.where((player) => player.isActive).length;
  } catch (e) {
    log.error(e);
    return 0;
  }
}

如果使用原始的 Future 方式的話,那代碼簡直是無法直視 —— 一般人要理清這樣的業(yè)務(wù)邏輯非常困難。

// 錯(cuò)誤示例
Future<int> countActivePlayers(String teamName) {
  return downloadTeam(teamName).then((team) {
    if (team == null) return Future.value(0);

    return team.roster.then((players) {
      return players.where((player) => player.isActive).length;
    });
  }).catchError((e) {
    log.error(e);
    return 0;
  });
}

不要使用沒必要的 async

很容易習(xí)慣在有異步操作的函數(shù)定義時(shí)使用 async。但是,在某些情況下卻是多余的。如果移除 async 對函數(shù)的行為沒有影響的話,那么就大膽地移除吧。

// 正確示例
Future<int> fastestBranch(
    Future<int> left, Future<int> right) {
  return Future.any([left, right]);
}

// 錯(cuò)誤示例
Future<int> fastestBranch(Future<int> left, Future<int> right) async {
  return Future.any([left, right]);
}

下面是使用 async 的幾個(gè)場景:

  • 在函數(shù)內(nèi)部有使用到 await —— 這是最常見的情況。
  • 需要異步返回一個(gè)錯(cuò)誤。使用 async 后再拋出錯(cuò)誤會比使用 return Future.error(...) 更簡潔。
  • 如果像用 Future 對象包裹一個(gè)返回值,那么使用 async 會比 Future.value(...) 更加簡潔。
// 正確示例
Future<void> usesAwait(Future<String> later) async {
  print(await later);
}

Future<void> asyncError() async {
  throw 'Error!';
}

Future<void> asyncValue() async => 'value';

避免直接使用 Completer

很對異步編程的新手想寫代碼嘗試產(chǎn)生一個(gè) Future 對象,而 Future 的構(gòu)造方法似乎滿足不了他們的需要,然后他們就會使用 Completer 類來做這樣的事情。

// 錯(cuò)誤示例
Future<bool> fileContainsBear(String path) {
  var completer = Completer<bool>();

  File(path).readAsString().then((contents) {
    completer.complete(contents.contains('bear'));
  });

  return completer.future;
}

Completer適用于兩類底層代碼:新的異步原語和不使用 Future 對象的異步接口。大部分代碼都應(yīng)該使用 async / await 或者 Future.then,這是因?yàn)樗麄兏忧逦?,而且也更容易處理錯(cuò)誤。比如下面的代碼會更加清晰簡潔,個(gè)人來說,會更喜歡 async /await的形式。

// 正確示例1:使用 Future.then
Future<bool> fileContainsBear(String path) {
  return File(path).readAsString().then((contents) {
    return contents.contains('bear');
  });
}

// 正確示例2:使用 async / await
Future<bool> fileContainsBear(String path) async {
  var contents = await File(path).readAsString();
  return contents.contains('bear');
}

當(dāng)處理 FutureOr<T>這種類型時(shí),務(wù)必記得檢查這個(gè)是Future<T>還是對象

FutureOr<T> 聲明的對象可能是 Future<T> 或僅僅是 T 類型的對象。因此,在處理這種類型時(shí),通常需要使用 is 檢查它到底是Future 對象還是普通對象。對于 FutureOr<int>這類特殊的類型來說,使用is intis Future<int>沒什么區(qū)別。但是,如果值的類型是 Object或是一個(gè)使用 Object實(shí)例化的類型參數(shù),那么就有區(qū)別了。這是因?yàn)槿绻褂?is T 判斷的話,Future<T>對象因?yàn)橐彩?Object,結(jié)果會返回 true,比如下面的例子:

// 正確示例
Future<T> logValue<T>(FutureOr<T> value) async {
  if (value is Future<T>) {
    var result = await value;
    print(result);
    return result;
  } else {
    print(value);
    return value;
  }
}

// 錯(cuò)誤示例
Future<T> logValue<T>(FutureOr<T> value) async {
  if (value is T) {
    print(value);
    return value;
  } else {
    var result = await value;
    print(result);
    return result;
  }
}

錯(cuò)誤示例中,如果傳過去的參數(shù)是Future<Object>的話,那么logValue 這個(gè)函數(shù)會將它當(dāng)做沒有使用 Future 包裹的原始對象進(jìn)行處理,結(jié)果導(dǎo)致程序錯(cuò)誤。因此,對 FutureOr<T>是否是異步對象判斷時(shí),應(yīng)當(dāng)先使用 is Future<T>判斷,次序不要弄錯(cuò)了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容