Thursday, September 05, 2013

promises, promises

YUI recently added a cool Promises module that I've been using the last couple weeks to implement client-side API's for a webapp. I like the way the code has come together, and I have a couple observations to record.

First, code that uses Promises is viral - it's natural for code that uses a promise-based API to itself use promises. Promises are a kind of monad like Haskell's IO() construct and Scala's Option class - which are also viral in my experience. For example, one of the first promise-based methods I wrote retrieved and compiled a handlebars template from the server:

        /**
         * Little helper loads and compiles a handlebar template via an AJAX call
         * 
         * @method loadHandlebar
         * @param url {string}
         * @return {Promise} Y.Promise delivers compiled template function or error info
         * @static
         */
        function loadHandlebar( url ) {            
            var promise = new Y.Promise( function(resolve,reject) {
                var ioconf = {
                  method:"GET",
                  on: {
                    success:function( id, resp, args ){
                        resolve( Y.Handlebars.compile( resp.responseText ) );
                    },
                    failure:function( id, resp, args ) {
                        log.log( "Failed to load " + url + ": " + resp.status + ", " + resp.statusText );
                        reject( resp );
                    }
                  },
                  timeout:60000
                };
                Y.io( url, ioconf );
            });
            return promise;
        }

That worked great, and the next thing I wanted was to dynamically load a template into a view with something like
littleUtil.loadHandlebar( url ).then( function(template){ view.template = template; } )
, but then the view couldn't use the template at render time until the promise was fulfilled. Rather than do something like
render: function() { this.templatePromise.then( function(template) { ... do render } ) ... }
, I setup a factory method for allocating views that itself returns a promise, and let the factory's client decide how to resolve the promise - which worked well in the little application I've been building that runs several asynchronous data-setup operations at start-up anyway:

    /**
     * Factory for PageView instances - maries YUI class inheritance with Typescript type system
     * @class PageViewFactory
     */
    export var PageViewFactory = {
        /**
         * Returns a Promise - the view loads various configuration 
         * information (templates, whatever) at the template, and 
         * fulfills the promise when the data is ready.
         * @method newView
         * @static
         * @return {Promise{PageView}}
         */
        newView: function (): Y.Promise {
            return templatePromise.then((template) => {
                var view = new SimplePageView();
                view.template = template;
                return view;
            });
        }
    };

This code is written in typescript - which extends javascript with support for static types. I'm only just in the process of migrating to typescript 0.9.1 which includes generic types. Hopefully after the upgrade I'll be able to modify the newView signature to something like:
newView: function(): Y.Promise<PageView> { ... }
.

Finally, it's interesting to compare yui's Promises implementation to the akka Futures implementation in scala. In javascript's single-threaded runtime promises usually coordinate callback execution between operations involving asynchronous IO. In scala's multithreaded runtime futures (promises by another name) are also used to simplify the coordination of concurrently running computations.

Post a Comment