10

Dart 允许链接future 以依次调用多个异步方法,而无需嵌套回调,这很棒。

假设我们想先连接到像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 只分配给了一个局部变量,该变量不在后续.thens 的范围内。所以,redisClient.smembers实际上redisClient.get会抛出一个空指针异常。

显而易见的解决方法是将返回值保存在另一个具有函数作用域的变量中:

  Future<String> FirstValue(String indexKey)
  { 
    RedisClient redisClient = null;
    return RedisClient.connect(Config.connectionStringRedis)
      .then((RedisClient theRedisClient) 
          {
            redisClient = theRedisClient;
            return redisClient.exists(indexKey); 
          })
      .then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
      .then((Set<String> keys) => redisClient.get(keys.first))
      .then((String value) => "result: $value");    
  }

不幸的是,这使得代码更冗长,更不美观:现在有一个额外的辅助变量(theRedisClient),我们不得不用匿名函数替换其中一个 Lambda 表达式,添加一对花括号和一个return语句以及另一个分号。

既然这似乎是一种常见的模式,有没有更优雅的方法呢?有什么方法可以在链条的下游访问那些早期的中间体?

4

2 回答 2

7

您可以使用嵌套赋值来避免大括号和return

.then((RedisClient rc) => (redisClient = rc).exists(indexKey))
于 2014-02-28T05:21:28.657 回答
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"); 
    );
于 2014-02-28T09:12:10.087 回答