Writing Synchronous Wrappers for Asynchronous Methods in TeaVM

In my last post, I shared a little bit about the work I’ve been doing porting Codename One into Javascript using TeaVM. In that post I mentioned that TeaVM supports multithreading in the browser. In my opinion, this is a major advancement as it finally allows us to write code synchronously in the browser. I.e. NO MORE CALLBACK HELL!

This is great, however, all of the existing Javascript APIs work asynchronously so if we want to work with them synchronously, we need to write synchronous wrappers for them. I expect that, over time, we’ll develop a standard library of such wrappers, but for now, we may need to do some wrapping on our own.

Example: Synchronous Wrapper for XHR Requests

The following method wraps XMLHTTPRequest to allow us to load the contents of a URL as a byte array. A similar mechanism could be used to fetch the contents as text, or a blob.

public Uint8Array getArrayBuffer(String url){
    Window window = (Window)JS.getGlobal();
    final XMLHttpRequest req = window.createXMLHttpRequest();
    final Object lock = new Object();
    final boolean[] complete = new boolean[1];
    req.open("get", url, true);
    req.setOnReadyStateChange(new ReadyStateChangeHandler() {

        @Override
        public void stateChanged() {
            if ( req.getReadyState() == XMLHttpRequest.DONE ){

                new Thread() {
                    @Override
                    public void run() {
                        complete[0]=true;
                        synchronized(lock){
                            lock.notifyAll();
                        }
                    }
                }.start();
            }
        }
    });
    req.setResponseType("arraybuffer");
    req.send();

    while (!complete[0]){
        synchronized(lock){
            try {
                lock.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    if (req.getResponse() == null  ){
        System.out.println(req.getAllResponseHeaders());
        System.out.println(req.getStatusText());
        System.out.println("Failed to load resource "+url);
        System.out.println("Status code was "+req.getStatus());
        return null;
    }

    return window.createUint8Array(
            (ArrayBuffer)req.getResponse()), req.getResponseType());

}

See a neater version of this code as a Gist

A couple of things to notice:

  1. Inside stateChanged(), we spawn a new Thread because threading primitives like synchronized and notifyAll() won’t work directly running in a native Javascript callback (at the time of this writing).
  2. The loop with lock.wait() doesn’t block the main Javascript thread. It uses callbacks in the background to pause execution of this “thread” until the lock.notifyAll() method is called.

Now we can use this method directly in our Java code to load data from a remote server synchronously without blocking the UI thread, and without introducing callback hell into our code.

comments powered by Disqus