Great Minds Think Alike: Asynchronous Patterns

Isn’t writing synchronous code nice?

function do_stuff () {
  do_thing_one ();
  do_thing_two ();
  do_thing_three ();
}

But synchronous is bad! Bad, bad, bad. So then came async. The simple pattern is to pass a callback to the function you are calling. It’s not as bad in JS because we just can do this:

function do_stuff () {
  do_thing_one ( function (result) {
    do_thing_two ( function (result) {
      do_thing_three ( function (result) {
      });
    });
  });
}

I’ve left out error handling. That really depends on the library.. But i imagine its messy. Now let’s see GIO style async.

function do_stuff () {
  do_thing_one_async ( function (ar) {
    var result = do_thing_one_finish (ar);
    do_thing_two_async ( function (ar) {
      var result = do_thing_two_finish (ar);
      do_thing_three_async (function (ar) {
        var result = do_thing_three_finish (ar);
      });
    });
  });
}

I like this a lot better than how i’d do it in python. But wouldn’t it be nice if you could write async code something like this?

var do_stuff = async (function () {
  var result = yield do_thing_one ();
  yield do_thing_two ();
  yield do_thing_three ();
});

Or even:

var do_stuff = async (function () {
  var result = yield do_thing_one ();
  yield do_thing_two ();
  try{
    yield do_thing_three ();
  } catch (e) {
    print("Exception handled");
  }
});

You can in python. You can in vala.¬†And for JS? Well, I was going to say “now you can“. But while I was looking for a good Vala link, I noticed Alex already did something like this over a year ago. D’oh.

What would be really nice is if the async wrappers could be generated automatically by GI. I had a first stab at this by simply parsing the GIR xml with E4X and providing an alternative import mechanism (thanks for the suggestion jdahlin). However to get full coverage i’d have to consider interfaces and inspect every object that implements an interface as it lands in JavaScript land to ensure it is wrapped. Ew.

Tags: , ,

9 Responses to “Great Minds Think Alike: Asynchronous Patterns”

  1. I wrote libiris in C so that I can have a consistent asynchronous framework in any language. It also has a work-stealing scheduler that is 8x faster than GThreadPool. Futures, message-passing, etc too.

    http://git.dronelabs.com/iris

    I haven’t had any meaningful work on it in a few months because I’ve been writing a profiler. But once I have that, I’ll get started again.

    – Christian

  2. Johan Dahlin says:

    The async utility libraries we are using at litl are included in Bugzilla:
    https://bugzilla.gnome.org/show_bug.cgi?id=608450

    Hope they’ll help you, we’ve been using them successfully in large code bases.

  3. Zeeshan Ali says:

    The python example you linked to doesn’t look way too different than what you have to do in C. :) However your Vala examples here really do make it *easy*.

  4. Alberto Ruiz says:

    Woohoo! The Jc2k is back!

  5. John Carr says:

    zeenix, in an ideal world the python way is almost identical to the vala way, the blog post i linked to is about the sort of machinery we’d need to add to the bindings to make it like that “out of the box”.

  6. Jon says:

    The “yield” keyword feels messy to me. The ideal syntax imho would be something like

    map do [function1, function2, function3]

  7. John Carr says:

    @Jon, i’m unconvinced. How does your code work if you want to assess the return value of function 1 and then call 2 OR 3 depending on the outcome? How do you deal with exceptions? How do you do a for loop over the return values of 1 and call 2 for each one and then finally call function 3 just once?

  8. Doug says:

    The twisted way using generators is way nicer than anything else I’ve seen:


    @defer.inlineCallbacks
    def this_function_is_magic():
    foo = yield do_something_async1()
    bar = yield do_something_async2(foo)
    baz = yield do_something_async3(bar)
    defer.returnValue(baz)

    When you yield it goes off and asynchronously runs the function, collects the return value when it’s available then your function is called again at the yield point and the return value passed in.

  9. John Carr says:

    @Doug: +1

    In whorl, Defer.async == defer.inlineCallbacks, other than that it’s pretty much the same thing. And the code for it almost fits on my screen all at once :)

    In vala its the same again only it’s built into the language so async is a keyword just like static or virtual, and that means the returnValue hack isn’t needed.