Continuation-Passing Style in Javascript

(This page is a mirrored copy of an article originally posted on the LShift blog; see the archive index here.)

As part of my recent experiments with Javascript, I happened to want a sleep function. Interestingly, there isn’t one available in browser-side Javascript environments - instead, there are four functions

setInterval
clearInterval
setTimeout
clearTimeout

and they take functions to call after a certain number of milliseconds have gone by. This leads to programming in continuation-passing style (see here, here, here, here, and here):

// ... some code ...
// At this point, we want to pause for one second before continuing.
setTimeout(function () {
    // ... code here continues running after 1000 milliseconds have gone by.
}, 1000);

It seems that a (laudable!) design decision has been made for avoiding blocking operations in browser scripting languages. It’s not just sleep-a-likes, either: the decision affects the design of XmlHttpRequest, which is used in AJAX-like server callbacks. The way you receive the server’s response to an XmlHttpRequest message is by supplying a callback function. This again leads to a continuation-passing style of programming - see the definition of AJAJ.proxy in this file. Here’s an example of AJAJ.proxy in action, as part of a simple AJAX+JSON chat application:

function rpc(action, arg, k, kfail) {
    var urlPrefix = (new RegExp("(.*/)([^/]+)”)).exec(document.location)[1];
    AJAJ.proxy(urlPrefix + “chat-server/simple-chat.asp”)
        ({action: action, arg: arg}, k, kfail);
}

function consoleRpc(action, arg) {
    rpc(action, arg,
        function (r) { console.recordOutput(formatConsoleRpc(action, arg),
                                            JSON.stringify(r)); },
        function (e) { console.recordError(formatConsoleRpc(action, arg),
                                           JSON.stringify(e)); });
}

The two functions k and kfail passed to the rpc function serve as the success and failure continuations, respectively. If the XmlHttpRequest succeeds, then the function starting with “function (r) { ...” will be called with the response from the server; otherwise, the function starting with “function (e) { ...” will be called with an exception object that reports on the failure. We’re using closures to extend the available control structures of the language.