Futures

refractalize edited this page Apr 10, 2013 · 3 revisions

Pogoscript's async operator makes light work of executing asynchronous IO calls in serial, e.g.:

user = user details! (name: 'bob')
user.email = 'bobs.new.email@example.com'
user.save!

But you're more or less on your own if you want to run several asynchronous IO operations concurrently. Options have been to write your own asynchronous handlers, like fork or some kind of asynchronous for each, or to use one of the well known asynchronous libraries available. But it could be better.

So we introduce the future operator, ?, which, like the async operator ! can be applied to any function or method call that follows the asynchronous calling convention. But unlike the ! operator, the ? operator initiates the asynchronous IO operation but doesn't wait for it, and instead returns a future (like a promise, but see below). The future can be stored for later, and can yield a result later when useful, again, using the ! operator.

Here's an example, you want to download two URLs concurrently:

first url = 'http://example.com/1'
second url = 'http://example.com/2'

first future response = http.get? (first url)
second future response = http.get? (second url)

first response = first future response!
second response = second future response!

The HTTP requests can be started with the http.get?, but it's not until you actually yield to the future with ... future response! that the program actually waits for the responses to come back. This gives your program time to do other things, like further processing or launching other IO requests. But let's say you have lots of web pages you want to download:

urls = [...]
future pages = [http.get? (url), where: url <- urls]
pages = [future page!, where: future page <- future pages]

Which could be shortened to:

urls = [...]
pages = [future!, where: url <- urls, future = http.get? (url)]

Pretty tidy!

Yielding Multiple Times

Of course you should also be able to wait for a future multiple times, each time producing the same result (or indeed error!)

future page = http.get? (url)

...

page1 = future page!
page2 = future page!

Catching Errors

As usual with the async operator !, to catch errors you just wrap in try catch:

future page = http.get? (url)

try
    page = future page!
catch (error)
    console.log (error)

Promises

So why not common js promises? There is no good reason not to use promises, they're a powerful and elegant solution for asynchronous IO in regular JS code. But pogoscript is more than that so we don't need them, the ! and ? operators do just about all that promises do and make it all easier. But why not use them under the hood? Well again, no good reason. Pogoscript was originally designed to make writing highly asynchronous node.js apps easier, and to that end adopted node.js's conventional callback style. Either would do, and if there's apetite for promise support then I'd be happy to integrate it somehow, with compiler switches or macros, whatever.

We call them Futures in pogoscript because they're not promises, even though they serve the same purpose and have very similar semantics. Just so people don't get confused about such things.

Implementation

Invoking a future in pogoscript

http.get? (url)

Should compile to something like this:

future (function (callback) { http.get (url, callback); });

Where future is defined like this:

function future (action) {
    var operationComplete = false;
    var operationError, operationResult;
    var futureCallbacks = [];

    function callback (error, result) {
        operationComplete = true;
        operationError = error;
        operationResult = result;

        for (var n = 0; n < futureCallbacks.length; n++) {
            futureCallbacks[n](operationError, operationResult);
        }
    }

    action(callback);

    return function (callback) {
        if (operationComplete) {
            callback(operationError, operationResult);
        } else {
            futureCallbacks.push(callback);
        }
    };
}
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.