Trying async

  1. Why hxbolts?
  2. Trying promhx
  3. Trying thx.promise
  4. Trying task
  5. Trying continuation
  6. Trying async
  7. Trying hext-flow
  8. Finally, hxbolts

This library is somehow similar to continuation – it use haxe macro magic to be able to use C#-like async / await. Only 94 haxelib downloads and 27 github stars, I think this lib need more attention. Okay… Click… Press… Voila, 28 github stars 🙂 I will use version from github, however my code should work (I hope) with version from haxelib.

TL;DR – 🙂 Works like a charm. Still need some polishing, but can be used right now.

Let’s begin

Like continuation, you shoudn’t do anything special to transform function with callbacks in a function suitable to use with @await. Any function which accepts callback as last argument is suitable. But there is one small, but very nice feature. You can pass occurred exception as first argument of callback function! Wow!

function fetchText(url : String, callback : Dynamic -> String -> Void) : Void {
    var urlLoader = new URLLoader();

    var onLoaderError = function(e : Event) : Void {
        callback(e.type, null);
    };

    urlLoader.addEventListener(Event.COMPLETE, function(_) : Void {
        callback(null, Std.string(urlLoader.data));
    });

    urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onLoaderError);
    urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onLoaderError);

    try {
        urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
        urlLoader.load(new URLRequest(url));
    } catch (e : Dynamic) {
        callback(Std.string(e), null);
    }
}

Let magic begins:

@async
function fetchJson(url : String) : DynamicExt {
    [var result] = fetchText(url);
    return cast Json.parse(result);
}

One note about strange syntax (I’m talking about [var result] = asyncFunction()) – version from github has support for more traditional @await, but when I try to use it, it was broken (doesn’t work inside if operators), so I stick with old syntax. At time I writing this post I see commit from 2th june that should fix it.

I didn’t do anything special to handle exceptions, because it will just work as expected.

More magic:

@async
function sendApiRequest(method : String) : DynamicExt {
    [var result] = fetchJson('http://some.api/${method}');

    if (result["error"] != null) {
        throw result["error"].asString();
    }

    return result;
}

@async
private function syncState() : Void {
    [var result] = sendApiRequest("sync-state");
    updateStateFromTheResponse(result);

    if (result.exists("playerAvatarUrl")) {
        [var bitmapData] = fetchBitmapData(result["playerAvatarUrl"].asString());
        setPlayerAvatar(bitmapData);
    }

    if (result.exists("opponentAvatarUrl")) {
        [var bitmapData] = fetchBitmapData(result["opponentAvatarUrl"].asString());
        setOpponentAvatar(bitmapData);
    }
}

Just like non-async code. And it just works!

Finally, you can easilly use @async function from non-async code:

function doTheTask() : Void {
    showLoader();

    syncState(function(err : Dynamic) : Void {
        hideLoader();

        if (err == null) {
            onTaskSuccessed();
        } else {
            showErrorPopup();
        }
    });
}

Pros

  • An interesting approach.
  • Async code looks like non-async.
  • It works!

Cons

  • Version 0.3.0 didn’t have support for @await, and you must use tricky syntax.
  • You can’t put asynchronous calls inside conditions. Indeed, it not so easy to add support for it, but it will be nice feature to have.

Conclusion

Very promising, works like a charm and can be used right now.

Source code

Leave a Reply

Your email address will not be published. Required fields are marked *

*