ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[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,它会错误地将其视为一个简单的同步值。