[TOC]
Dart有幾個語言特性來支持異步編程。以下最佳實踐適用于異步編碼。
## 優先使用async/await代替原始的futures
異步代碼是出了名的難以閱讀和調試,即使是在使用像futures這樣的抽象時也是如此。async/await語法提高了可讀性,允許您在異步代碼中使用所有Dart控制流結構。
以下是推薦的示例:
~~~
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<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;
});
}
~~~
## 當異步沒有任何用處時,不要使用它。
很容易養成在任何與異步相關的函數上使用異步的習慣。但在某些情況下,它是無關的。如果可以在不改變函數行為的情況下省略異步,那么就這樣做。
以下是推薦示例:
~~~
Future afterTwoThings(Future first, Future second) {
return Future.wait([first, second]);
}
~~~
以下是不推薦的示例:
~~~
Future afterTwoThings(Future first, Future second) async {
return Future.wait([first, second]);
}
~~~
使用異步的情況包括:
* 您使用的是await。(這是顯而易見的。)
* 您正在異步返回一個錯誤。異步然后拋出比返回Future.error(…)短。
* 您正在返回一個值,并且希望它隱式地包裝在future。異步比Future.value(…)短。
~~~
Future usesAwait(Future later) async {
print(await later);
}
Future asyncError() async {
throw 'Error!';
}
Future asyncValue() async => 'value';
~~~
## 考慮使用高階方法轉換流。
這與上面關于迭代的建議相似。Streams支持許多相同的方法,并且正確地處理傳輸錯誤、關閉等問題。
## 避免直接使用Completer。
許多不熟悉異步編程的人都希望編寫能夠創造future的代碼。Future的構造函數似乎不適合它們的需要,所以它們最終找到Completer類并使用它。
以下是錯誤示例:
~~~
Future<bool> fileContainsBear(String path) {
var completer = Completer<bool>();
File(path).readAsString().then((contents) {
completer.complete(contents.contains('bear'));
});
return completer.future;
}
~~~
有兩種底層代碼需要Completer:新的異步原語和不使futures的異步代碼接口。大多數其他代碼應該使用async/await或Future.then(),因為它們更清晰,并且讓錯誤處理更容易。
~~~
Future<bool> fileContainsBear(String path) {
return File(path).readAsString().then((contents) {
return contents.contains('bear');
});
}
~~~
~~~
Future<bool> fileContainsBear(String path) async {
var contents = await File(path).readAsString();
return contents.contains('bear');
}
~~~
## 在消除 FutureOr\<T\>(其類型參數可以是對象)的歧義時,對Future\<T\>進行測試。
在使用FutureOr\<T\>執行任何有用的操作之前,通常需要檢查是否有Future\<T\>或明確的T。如果type參數是某個特定類型,如FutureOr\<int> ,使用哪個測試無關緊要,是int還是Future\<int>。 兩者都有效,因為這兩種類型是不相交的。
但是,如果值類型是Object或可能使用Object實例化的類型參數,則兩個分支重疊。 Future\<Object>本身實現了Object,因此是Object或者是T,其中T是一些可以用Object實例化的類型參數,即使對象是future也會返回true。 相反,明確測試Future案例:
~~~
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 as T;
}
}
~~~
錯誤用法示例:
~~~
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;
}
}
~~~
在這個糟糕的示例中,如果您向它傳遞一個Future,它會錯誤地將其視為一個簡單的同步值。