Dart Chain Futures - Can I Access Intermediate Results?

Dart allows a chain of futures to invoke several asynchronous methods sequentially without inserting callbacks, which is surprising.

Say we want to first connect to a data store, such as Redis , and then start a bunch of sequential readings:

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"); } 

Four asynchronous methods, but the code is pretty easy to read and understand. It looks like the steps are executed synchronously and sequentially. Nice! (Imagine you need to write the same code using nested JavaScript callbacks ...)

Unfortunately, this will not work: RedisClient, which we get from the .connect method, is assigned only to a local variable, which is not possible for subsequent .then s. Thus, redisClient.smembers and redisClient.get will actually throw a null pointer exception.

The obvious fix is ​​to save the return value in another variable with scope:

  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"); } 

Unfortunately, this makes the code more verbose and less beautiful: now there is an additional helper variable (theRedisClient), and we had to replace one of the Lambda expressions with an anonymous function, adding a pair of curly braces and return and another semicolon.

Since this seems to be a common model, is there a more elegant way to do this? Any way to access those early intermediates down the chain?

+7
dart dart-async
source share
2 answers

You can use nested assignment to avoid curly braces and return :

 .then((RedisClient rc) => (redisClient = rc).exists(indexKey)) 
+4
source share

You can also use frames with futures, without putting all the "that" calls on the same level. I would do something like:

 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"); ); 

Indentation is always difficult with such code. This example follows the Dart style guide, but I think it could be more readable with less indentation then calls:

 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"); ); 
+3
source share

All Articles