Finally, hxbolts

  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

And finally, I’ll make the same task using hxbolts (few words about – it is pure haxe port of java library Bolts, which itself seems to be inspired by Task Parallel Library from dotNET).

TL;DR – 🙂 Strongly typed, easy to use. Without all this async / await magic, but predictable and well tested.

Let’s begin

Again and again I begin with old-good fetchText() function. In this library promise called Task and handle to promise called TaskCompletionSource (not the best naming ever, but works). I don’t need to use any hacks with Timer.delay() – it just works as expected.

function fetchText(url : String) : Task<String> {
    var tcs = new TaskCompletionSource<String>();
    var urlLoader = new URLLoader();

    var onLoaderError = function(e : Event) : Void {
        tcs.setError(e.type);
    };

    urlLoader.addEventListener(Event.COMPLETE, function(_) : Void {
        tcs.setResult(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) {
        tcs.setError(Std.string(e));
    }

    return tcs.task;
}

Rest is easy – just use onSuccess() to chain tasks. One note – onSuccess() accepts not result of previous operation, but rather task of previous operation. It confusing a little, but this is done to be consistent with continueWith() function.

Library will take care about any thrown exceptions, so I don’t need to do anything special.

function fetchJson(url : String) : Task<DynamicExt> {
    return fetchText(url).onSuccess(function(task : Task<String>) : DynamicExt {
        return cast Json.parse(task.result);
    });
}

function sendApiRequest(method : String) : Task<DynamicExt> {
    return fetchJson('http://some.api/${method}').onSuccess(function(task : Task<DynamicExt>) : DynamicExt {
        if (task.result["error"] != null) {
            throw task.result["error"];
        }

        return task.result;
    });
}

Next function is syncState(). Task.whenAll() will wait when all sub-tasks were completed (either resolved or rejected), so I don’t need to do anything special to deal with cases when one task still running, but other already failed. Everything just works as expected.

function syncState() : Task<Void> {
    return sendApiRequest("sync-state").onSuccessTask(function(task : Task<DynamicExt>) : Task<Void> {
        var subTasks = new Array<Task<Void>>();
        updateStateFromTheResponse(task.result);

        if (task.result.exists("playerAvatarUrl")) {
            subTasks.push(fetchBitmapData(task.result["playerAvatarUrl"].asString()).onSuccess(function(t : Task<BitmapData>) : Void {
                setPlayerAvatar(t.result);
            }));
        }

        if (task.result.exists("opponentAvatarUrl")) {
            subTasks.push(fetchBitmapData(task.result["opponentAvatarUrl"].asString()).onSuccess(function(t : Task<BitmapData>) : Void {
                setOpponentAvatar(t.result);
            }));
        }

        return Task.whenAll(subTasks);
    });
}

Finally:

function doTheTask() : Void {
    showLoader();

    syncState().continueWith(function(task : Task<Void>) : Void {
        hideLoader();

        if (task.isSuccessed) {
            onTaskSuccessed();
        } else {
            showErrorPopup();
        }
    });
}

Pros

As an author of the port, I can not be objective, so forgive me laudatory odes… Ok, will be more serious.

  • Everything is strongly typed.
  • Support for exceptions.
  • You don’t need to write workarounds to achieve result – it just works as expected.
  • Everything it tested with unit tests.
  • Although I don’t use this now, can execute tasks on different threads, and everything should be thread-safe.

Cons

  • Not the best method naming ever – Task (instead of Promise), TaskCompletionSource, onSuccess (instead of then), etc.
  • Unit tests use Math.random() inside, which makes them less predictable.

In my humble opinion

  • Would be nice to have support for async / await. Maybe in version 4.0…

Conclusion

In my hubmle opinion, a worthy competitor to other libraries. Strongly typed, predictable and well tested.

Source code

Leave a Reply

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

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.