ES7 await

It’s tempting to start exploring await keyword expected in ECMAScript 7 standard. We’ve tried to overcome callback hell with libraries, async.js and its variations, transpilations (sweet.js, IcedCoffeeScript) and finally Promises, which seem to be quite good finish of that research. But, still, Promises are just a library, even if standard native library, therefore there is some implementation leaking up to the business code (.then, .all etc). Going forward, ES6 yield + spawn are leaky abstraction as well and in fact a hack, because they were not designed for asynchronous calls without callback functions.

await polyfill

Le’t play in await implementators. What do we need? Hmm, support for await keyword? Let’s try:

await Promise.resolve(true); // Uncaught SyntaxError: Unexpected identifier

Well, we can’t parse await even if using esprima#harmony. Hmm, is this a problem really? We can use… function!

function await(promise) {
    // logic will be handled by VM
}

await(Promise.resolve(true));

And viola, that was a bit tricky but trivial to do. Right after esprima implements await as keyword, we’ll be able to quickly switch to keyword instead of function call. Let’s make one more nice thing:

await.toString = function() { 
    return "[native code]";
}

just to make impression that we deal with something real 🙂

Let’s create a function that creates a Promise for HTTP GET, it is normal, native JavaScript function:

function httpGet(filePath) {
  return new Promise(function (success, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
      if (xhr.status === 200) {
        success(xhr.responseText);
      } else {
        reject("File not found!");
      }
    };
    xhr.open("GET", "/proxy/?" + filePath);
    xhr.send();
  })
}

and we want to use it the context like this:

function collectResults() {
    var results = [], urls = [
            'http://google.com',
            'http://yahoo.com',
            'http://facebook.com'
        ];
    for (var i in urls) {
        var url = urls[i];
        console.log('starting download ' + url);
        results.push(await(httpGet(url)));
        console.log('just downloaded ' + url);
    }
    console.log(results);
}
collectResults();

and the expected result is:

starting download http://google.com
just downloaded http://google.com
starting download http://yahoo.com
just downloaded http://yahoo.com
starting download http://facebook.com
just downloaded http://facebook.com

Of course, it won’t work in native JavaScript, that’s why we have to convert it into metacircular JavaScript function with special support for await keyword. I’d like to use that form:

var awaitifiedFunction = addAwaitSupport(collectResults);
awaitifiedFunction();

There is a new function, addAwaitSupport, let’s see:

function addAwaitSupport(fn) {
    function interceptor(e, value, env, pause) {
        if (e.type === 'CallExpression' && value && value.callee === await) {
            var resume = pause();
            // args[0] contains promise object passed to the await. We are in the VM now and we decide what to do next
            value.args[0].then(resume);
        }
    }
    var env = {
            console: console,
            await: await,
            httpGet: httpGet
        };
    return metaes.evaluate(fn, env, { interceptor: interceptor });
}

You may notice additional parameter: pause. This is a function that whenever called, the interpreter stops and the function returns resume function, like this:

var resume = pause();
// later...
resume();

The resume is called once the Promise is resolved. Additionally, the resume gets resolved value from the Promise and this is what we want to have.
By the way, this concept is called call-cc (call with current continuation) and we’ve accidentaly recreated it in JavaScript. If you still remember, in the initial introduction post I’ve mentioned that MetaES is created using CPS style – there are continuations present as well, of course.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: