Tuesday, January 3, 2012

Dart Futures

‹prev | My Chain | next›

So far in my exploration of Dart I have mostly confined myself to familiar territory. Tonight I begin playing with some of the new stuff that the language brings to the table, starting with Futures.

I know that node.js used to have promises, but they were removed very early on in the project as unnecessary. I am curious why Dart elevates a thing to first order construct when it was deemed unneeded in node, but mostly I would simply like to get it working.

I much appreciate the Dart lang documentation suggesting that I get a future from "somewhere" (getFutureFromSomewhere()). Fortunately, it does not take too much hunting to find a somewhere in the core language—Completer (benefits of picking up a language when it is still small).

The completer needs a type, so I try strings:
main() {
  var completer = new Completer<String>();
}
That compiles, so I know that I am not completely lost. Next up, I grab the future from my completer:
main() {
  var completer = new Completer<String>();
  var future = completer.future;
}
Again, it compiles, so I think I am OK.

I have my future and completer, that means it is time to do something with both. Successful completion will invoke the then() message on the future with whatever message the completer sends. I also need to trigger the completion by calling complete() on the completer:
main() {
  var completer = new Completer<String>();

  var future = completer.future;
  future.then((message) {
    print("Future completed with message: " + message);
  });

  completer.complete("foo");
}
(try.dartlang.org)

Yay! That works:
➜  command_line git:(master) ✗ dart futures01.dart
Future completed with message: foo
Ah, interesting. The call to then() on the future is supplying an anonymous function as a callback to be invoked when the future is done. The anonymous function syntax in Dart makes it read almost like defining the then() method directly on the future object. I think I am beginning to appreciate Dart's function syntax.

Futures promise to be quite useful with the eventful nature of web applications. I will explore that another day. For now, I try a few other things like passing a second complete() message to the completer:
main() {
  var completer = new Completer<String>();

  var future = completer.future;
  future.then((message) {
    print("Future completed with message: " + message);
  });

  completer.complete("foo");
  completer.complete("bar");
}
(try.dartlang.org)

Locally that results in a "future already completed exception:
➜  command_line git:(master) ✗ dart futures01.dart
Future completed with message: foo
Unhandled exception:
Exception: future already completed
 0. Function: 'FutureImpl._setValue@924b4b8' url: 'bootstrap_impl' line:3143 col:7
 1. Function: 'CompleterImpl.complete' url: 'bootstrap_impl' line:3173 col:26
 2. Function: '::main' url: '/home/cstrom/repos/dart-site/examples/command_line/futures01.dart' line:10 col:21
Interestingly, that exception comes from the completer, not the future, but the message indicates that it is a future error.

Speaking of errors, it is also possible to signal the future that an exceptional condition has occurred:
main() {
  var completer = new Completer<String>();

  var future = completer.future;
  future.then((message) {
    print("Future completed with message: " + message);
  });

  var exception = new Exception("Too awesome");
  completer.completeException(exception);
}
That results in a runtime error because the future does not know how to handle it:
➜  command_line git:(master) ✗ dart futures02.dart
Unhandled exception:
Exception: Too awesome
 0. Function: 'FutureImpl._complete@924b4b8' url: 'bootstrap_impl' line:3136 col:9
 1. Function: 'FutureImpl._setException@924b4b8' url: 'bootstrap_impl' line:3158 col:14
 2. Function: 'CompleterImpl.completeException' url: 'bootstrap_impl' line:3177 col:30
 3. Function: '::main' url: '/home/cstrom/repos/dart-site/examples/command_line/futures02.dart' line:10 col:30
To prevent the runtime error, I need to define a handleException() method and signal that all is well by returning true from that callback:
main() {
  var completer = new Completer<String>();

  var future = completer.future;
  future.then((message) {
    print("Future completed with message: " + message);
  });
  future.handleException((e) {
    print("Handled: " + e);
    return true;
  });

  var exception = new Exception("Too awesome");
  completer.completeException(exception);
}
(try.dartlang.org)

That results in:
➜  command_line git:(master) ✗ dart futures02.dart
Handled: Exception: Too awesome
Nice! There is certainly some power in these little beasties.

Day #254

3 comments:

  1. I don't know why node maintainers did remove futures. It sounded like an interesting concept.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Nice post, this sure can be an unfamiliar territory for many programmers new to Dart. I liked your simple and straight forward examples.

    For people new to Dart trying this stuff out I'd like to point out that Dart's string interpolation syntax has change a bit so you need to change some rows to look like this:
    print("Handled: + $e");
    instead of this:
    print("Handled: " + e);

    ReplyDelete