Awaiting multiple levels of Futures?

Viewed 1230

I have a getter for a singleton instance of a SQFlite Database, like this:

static Database _db;
static Future<Database> get db async {
    if( _db == null )
        _db = await openOrCreateDatabase();
    return _db;
}

Now, I want to perform a query on the db:

final List<Map<String, dynamic>> rows = await (await db).query(
    'mytable', 
    where: 'id<?', 
    whereArgs: [10]
);

I find the await (await db) ugly - is there a nicer way to "chain" two await's together in serial?

2 Answers

Since both operations returns a Future there is no other way that await for the two operations. To await multiple futures with a single await you need to add it to a list of futures and then use Future.wait but generally is more ugly and useless in this case.

      var futures = <Future>[];
      futures.add(operationThatReturnAFuture());
      futures.add(operationThatReturnAFuture());
      await Future.wait(futures);

Dart does not support this kind of chaining between await but i understand that looks odd on this case.

Maybe if you change the variable rows to a method the code will look a little cleaner.

For example something like:

getUser(int id) async {
    final db = await database;
    var res = await  db.query("User", where: "id = ?", whereArgs: [id]);
    return res.isNotEmpty ? User.fromMap(res.first) : Null ;
  }

An alternative is to use the old syntax then():

Future getDb() => db;
Future makeQuery(db) => db.query(
    'mytable', 
    where: 'id<?', 
    whereArgs: [10]
);

void doSomethingWithQuery(query){}

getDb().then(makeQuery).then(doSomethingWithQuery);

The code reads naturally this way, "get the database, then make a query, then do something else". I don't think async keyword is needed for getDb() and makeQuery(db) but I might be wrong.

As a side note, the get db method could be streamlined using the null-aware ??= operator:

static Future<Database> get db async => _db ??= await openOrCreateDatabase();
Related