Wait for future to complete

35.1k Views Asked by At

I use my postgres database query to determine my next action. And I need to wait for the results before I can execute the next line of code. Now my conn.query returns a Future but I can't manage to get it async when I place my code in another function.

main() {
  // get the database connection string from the settings.ini in the project root folder 
  db = getdb();
  geturl().then((String url) => print(url));
}

Future geturl() {
  connect(db).then((conn) {
    conn.query("select trim(url) from crawler.crawls where content IS NULL").toList()
      .then((result) { return result[0].toString(); })
      .catchError((err) => print('Query error: $err'))
      .whenComplete(() {
        conn.close();
      });
  });
}

I just want geturl() to wait for the returned value but whatever I do; it fires immediately. Can anyone point me a of a piece of the docs that explains what I am missing here?

4

There are 4 best solutions below

1
On BEST ANSWER

You're not actually returning a Future in geturl currently. You have to actually return the Futures that you use:

Future geturl() {
  return connect(db).then((conn) {
    return conn.query("select trim(url) from crawler.crawls where content IS NULL").toList()
      .then((result) { return result[0].toString(); })
      .catchError((err) => print('Query error: $err'))
      .whenComplete(() {
        conn.close();
      });
  });
}
1
On

I prefer, in scenarios with multiple-chained futures (hopefully soon a thing of the past once await comes out), to use a Completer. It works like this:

 Future geturl() {
   final c = new Completer();  // declare a completer.

   connect(db).then((conn) {
     conn.query("select trim(url) from crawler.crawls where content IS NULL").toList()
       .then((result) { 
           c.complete(result[0].toString()); // use the completer to return the result instead
        })
       .catchError((err) => print('Query error: $err'))
       .whenComplete(() {
         conn.close();
       });
   });

   return c.future;  // return the future to the completer instead
 }
0
On

To elaborate on John's comment, here's how you'd implement this using async/await. (The async/await feature was added in Dart 1.9)

main() async {
  try {
    var url = await getUrl();
    print(url);
  } on Exception catch (ex) {
    print('Query error: $ex');
  }
}

Future getUrl() async {
  // get the database connection string from the settings.ini in the project root folder 
  db = getdb();
  var conn = await connect(db);
  try {      
    var sql = "select trim(url) from crawler.crawls where content IS NULL";
    var result = await conn.query(sql).toList();
    return result[0].toString();
  } finally {
     conn.close();
  }
}
2
On

To answer your 'where are the docs' question: https://www.dartlang.org/docs/tutorials/futures/

You said that you were trying to get your geturl() function to 'wait for the returned value'. A function that returns a Future (as in the example in the previous answer) will execute and return immediately, it will not wait. In fact that is precisely what Futures are for, to avoid code doing nothing or 'blocking' while waiting for data to arrive or an external process to finish.

The key thing to understand is that when the interpreter gets to a call to then() or 'catchError()' on a Future, it does not execute the code inside, it puts it aside to be executed later when the future 'completes', and then just keeps right on executing any following code.

In other words, when using Futures in Dart you are setting up chunks of code that will be executed non-linearly.