Dart允许chaining futures按顺序调用多个异步方法而不嵌套回调,这很棒。
假设我们想首先连接到Redis之类的数据存储,然后运行一系列顺序读取:
Future<String> FirstValue(String indexKey)
{
return RedisClient.connect(Config.connectionStringRedis)
.then((RedisClient redisClient) => redisClient.exists(indexKey))
.then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
.then((Set<String> keys) => redisClient.get(keys.first))
.then((String value) => "result: $value");
}
四种异步方法,但代码相当容易阅读和理解。几乎看起来步骤是同步并按顺序执行的。美丽! (想象一下,必须使用嵌套的JavaScript回调编写相同的代码......)
不幸的是,这不会完全工作:我们从.connect
方法获得的RedisClient仅被分配给一个不在后续{{1}范围内的局部变量}秒。因此,.then
和redisClient.smembers
实际上会抛出空指针异常。
显而易见的解决方法是将返回值保存在另一个带有函数范围的变量中:
redisClient.get
不幸的是,这使得代码更加冗长,更不美观:现在有一个额外的辅助变量(theRedisClient),我们不得不用一个匿名函数替换一个Lambda表达式,添加一对花括号和一个{{ 1}}语句和另一个分号。
由于这似乎是一种常见的模式,有没有更优雅的方式来做到这一点?有没有办法在链中进一步访问那些早期的中间体?
答案 0 :(得分:6)
您可以使用嵌套作业来避免花括号和return
:
.then((RedisClient rc) => (redisClient = rc).exists(indexKey))
答案 1 :(得分:3)
您也可以通过不将所有'then'调用放在同一级别来对期货做范围。 我会这样做:
Future<String> FirstValue(String indexKey) =>
RedisClient.connect(Config.connectionStringRedis)
.then((RedisClient redisClient) =>
redisClient.exists(indexKey)
.then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
.then((Set<String> keys) => redisClient.get(keys.first))
.then((String value) => "result: $value");
);
使用这样的代码缩进总是很困难。此示例遵循Dart样式指南,但我认为它可以更具可读性,减少then
调用的缩进:
Future<String> FirstValue(String indexKey) =>
RedisClient.connect(Config.connectionStringRedis)
.then((RedisClient redisClient) =>
redisClient.exists(indexKey)
.then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
.then((Set<String> keys) => redisClient.get(keys.first))
.then((String value) => "result: $value");
);