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?
dart dart-async
Max
source share