[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,它会错误地将其视为一个简单的同步值。