-
Notifications
You must be signed in to change notification settings - Fork 10
Futures
Pogoscript's async operator makes light work of executing asynchronous IO calls in serial, e.g.:
user = user details! (name: 'bob')
user.email = '[email protected]'
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!
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!
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)
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.
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);
}
};
}
According to the documentation, you can have Closure Compiler add the sourceMappingURL to the bottom of the script with something like this:
--output_wrapper "%output%
//# sourceMappingURL=output.js.map"
being added to your call. Not that you cannot use "\n" here, and you need a newline literal. On a Linux shell this works just fine (if you're inside of quotes when you press enter, the command doesn't get executed).