Who says JavaScript I/O has to be ugly?

I’m excited to announce the latest version of jsTask, which now supports first-class, synchronizable events. A Sync object represents an event that may or may not have fired yet, and which a task can block on.

jsTask already makes it easy to write tasks that block on I/O:

var foo = yield read("foo.json");
var bar = yield read("bar.json");

But the power lurking behind that code is the fact that read actually returns a Sync value, which can be used to build more interesting synchronizable events. For example, we can do a join on several concurrent operations, so that one doesn’t have to wait for the other before initiating:

var [ foo, bar ] = yield join(read("foo.json"), read("bar.json"));

Or we can choose from several different concurrent operations, letting whichever completes first produce the result (and cancelling the others):

var file = yield choose(read("a.json"), read("b.json"), read("c.json"));

With combinators like these, you can start to build interesting idioms, such as the timeout method, which I went ahead and built in to the library:

try {
    var file = yield read("foo.json").timeout(1000);
} catch (e) {
    // I/O error or timed out
}

This is just the beginning: I’ll be implementing Sync wrappers for all the major DOM I/O events, and I’ll keep experimenting with API’s for common use cases and helpful idioms.

I believe jsTask would also be useful for server-side JS frameworks like node.js, which use non-blocking I/O heavily. But first we have to get generators into V8 and ECMAScript!

8 Responses to Who says JavaScript I/O has to be ugly?

  1. …or get spidermonkey running in Node.js…

  2. Thanks for this library! =) I especially like the look of the concurrent operations examples.

    I’m interested to know what you think of this approach in regards to performance ? Async non-blocking I/O obviously has inherent performance benefits with JS being single threaded in that other operations can continue. Does overuse of this library risk wasting CPU cycles or am I missing something ?

    For example with Node much of its famed performance, apart from V8 awesomeness, comes from its non-blocking/async approach. Do you suggest using it for only some of non performance critical server-side code ?

    I’m not saying jstask is a bad idea, I’m just trying to understand the best use-cases for it esp regarding any impact on performance.

  3. Hannes Wallnöfer

    Right on! This looks very similar to what Havoc Pennington has proposed and is implemented in my Stick framework. I full-heartedly agree about getting generators into V8 and ES.

  4. Have you seen Reactive Extensions (Rx) for Javascript? The what jsTask does is at the core of Rx
    http://msdn.microsoft.com/en-us/data/gg577609

    Can you comment on the differences in the design and/or goals?

  5. @Isaac: You certainly can abuse the library by blocking program logic unnecessarily. But a nice thing is that “blocking” doesn’t actually jank the browser event queue; it only blocks your one task. And another nice thing is that blocking is still very explicit because it always involves the yield keyword. So it’s not possible to accidentally call a blocking API without realizing it.

    This library should work in conjunction with the non-blocking async approach, not instead of it. Even in Node, you still have operations that can’t continue until they receive results from I/O. This library makes those operations more straightforward to express.

  6. @Hannes: Thanks! I look forward to checking out your Stick framework.

  7. @Chris: Rx is based on a slightly different model than jsTask. Rx’s model is known as functional reactive programming (FRP). The core concept there is a (possibly infinite) stream of events, and you build abstractions by combining/mapping/filtering on streams. Another example of FRP for JavaScript is Flapjax.

    jsTask is based on a model that’s a little lighter-weight, where a Sync will typically represent just a single event, rather than a sequence of events. But both models have similarities, in that they try to abstract away the imperative style of event-driven programs. FRP is maybe a little more ambitious; it inverts the control flow of your whole program. But the benefit you get is a vastly simplified control-flow model. I really like Rx and I hope that it continues to pick up steam.

    My goal was really just to show that you can get a lot of simplification — particularly to eliminate the nested-callback problem — with maybe a bit less restructuring of your program. But people really should experiment with both styles! There’s no way to know for sure without testing it out on real programs.

  8. @dherman Thank you very much for your helpful response! I am confident I understand how to best utilize this in my projects now. Really appreciate your work on this, it should make certain code far tidier.