Asynchronous function chaining in JavaScript

Update, 25 February 2008: This class is now available as part of JS.Class (it’s called MethodChain now). It also forms a key part of Ojay, an expressive wrapper for YUI.

Update, 12 Dec 2007: Another implementation change. A blank ChainCollector instance now has the following properties: then, and, ____ (formerly __enqueue) and fire. The method queue and base object are private variables, and addMethods is a static method. I’m trying to make it as flexible as possible, i.e. it should have the fewest possible named properties but still allow you to extend it to your needs. Also, you now add methods to ChainCollector‘s prototype rather than to instances as this is much faster.

Update, 9 Dec 2007: I’ve modified the implementation of ChainCollector so it has fewer methods in its prototype, thus fewer chances of name collisions. It no longer has initialize, __addMethods or __enqueue methods — these are now ‘private’ methods created using the module pattern.

For this post, I’m going to be using jQuery, because it’s the closest well-known thing to the library I’m currently working on at my new job. (It’s a wrapper for YUI with lots of syntax niceties.)

Programming asynchronous actions is a pain in the head. Why can’t I do setTimeout($('#myNode').hide, 2000)? I need to bind the hide function to the $('#myNode') object for starters, and jQuery doesn’t give you a bind method, and besides, binding and execution scope gives lots of JavaScript novices a headache. Wouldn’t this be nice:

$('#myNode').wait(2).then.hide();

Over the weekend, I was trying to figure out a general purpose way of adding asynchronous behaviour like this, so I could use it with event handlers, Ajax calls, post-animation callbacks etc. My inspiration came from Methodphitamine (site unavailable, try the Google cache).

I won’t bore any non-Rubyists with the details, but the idea is quite simple: create an object with no predefined methods, which accepts any method call and adds the name of the method and its arguments to a queue. This queue can be turned into a function and called on whatever object you want at a later time. Unfortunately, JavaScript has no analogue for Ruby’s method_missing, which means if you want such an object in JavaScript, you need to predefine every method name you might want to use. Big pain.

But, you can get something quite usable if you’re implementing an interface (like jQuery’s) that allows chaining. You can pass some object to the constructor for your magical queue-collecting object, and have it implement copies of all the object’s methods. If you need methods from other objects, you can pass those in too.

An example, implementing my wait suggestion from above:

jQuery.fn.wait = function(time) {
  var collector = new ChainCollector(), self = this;
  // Deal with scoping issues...
  var fire = function() { collector.fire(self); };
  setTimeout(fire, Number(time) * 1000);
  return collector;
};

// Then extend ChainCollector with all jQuery's methods
ChainCollector.addMethods(jQuery);

With this defined (and ChainCollector, code to follow), you can do this:

$('#jq-header').wait(2).then.hide('slow')

Grab all the code from this post and go try that out on the jQuery home page. Nice, no? I’m using this technique for writing concise, extensible event handlers at the moment, and it’s very nice indeed. You can even chain multiple wait calls into the same statement:

$('#jq-header').wait(2).then.hide('slow').wait(3).then.show('fast')

Anyway, now that I’ve got you all fired up about this, here’s the magic that makes it all possible:

var ChainCollector = function(base) {
    var CLASS = arguments.callee;
    
    this.then = this.and = this;
    var queue = [], baseObject = base || {};
    
    this.____ = function(method, args) {
        queue.push({func: method, args: args});
    };
    
    this.fire = function(base) {
        var object = base || baseObject, method, property;
        for (var i = 0, n = queue.length; i < n; i++) {
            method = queue[i];
            if (object instanceof CLASS) {
                object.____(method.func, method.args);
                continue;
            }
            property = object[method.func];
            object = (typeof property == 'function')
                    ? property.apply(object, method.args)
                    : property;
        }
        return object;
    };
};

ChainCollector.addMethods = function(object) {
    var methods = [], property, i, n, name;
    var self = this.prototype;
    
    var reservedNames = [], blank = new this();
    for (property in blank) reservedNames.push(property);
    var re = new RegExp('^(?:' + reservedNames.join('|') + ')$');
    
    for (property in object) {
        if (Number(property) != property)
            methods.push(property);
    }
    if (object instanceof Array) {
        for (i = 0, n = object.length; i < n; i++) {
            if (typeof object[i] == 'string')
                methods.push(object[i]);
        }
    }
    for (i = 0, n = methods.length ; i < n; i++)
        (function(name) {
            if (re.test(name)) return;
            self[name] = function() {
                this.____(name, arguments);
                return this;
            };
        })(methods[i]);
    
    if (object.prototype)
        this.addMethods(object.prototype);
};

I’d love to know what uses people find for this.

Emulating JavaScript’s String#replace in Ruby

Update: turns out this is a lot easier than the method presented below: see Christoffer Sawicki’s implementation. Consider my method an example of what happens when you’re trying to port features from one language to another and end up looking in all the wrong places for a solution.

I know what you’re thinking: we already have String#gsub, why do we need something else? Well, while writing PackR, I found out that JavaScript’s String#replace is helpful in ways that Ruby can only dream of:

>>> var logger = function() { console.log(arguments); };
>>> 'something'.replace(/(..)(m?)/g, logger)

// Outputs...
["som", "so", "m", 0, "something"]
["et", "et", "", 3, "something"]
["hi", "hi", "", 5, "something"]
["ng", "ng", "", 7, "something"]

How generous is that? You get the matched string portion, each sub-pattern match within the current portion, the starting index of the match, and the full string. Everything you could possibly wish to know in order to process matches. All this information is available in Ruby, it’s just a bit of a pig to get it to work like it does up above.

The first thing we need to do is extend the Regexp class so that it can count the number of sub-patterns (bits enclosed by ()) in a regular expression (bits of this come straight from Packer):

class Regexp
  ESCAPE_CHARS = /\\\\./
  ESCAPE_BRACKETS = /\\(\\?[:=!]|\\[[^\\]]+\\]/
  BRACKETS = /\\(/

  def count
    expression = source.
        gsub(ESCAPE_CHARS, "").
        gsub(ESCAPE_BRACKETS, "")
    expression.scan(BRACKETS).length
  end
end

The second extension we need to make is to the String class. We need to be able to work out the index of every pattern match within a string, and we can do this using StringScanner (part of Ruby core):

require 'strscan'
class String
  def indexes(regexp)
    scanner, ary = StringScanner.new(self), []
    ary << scanner.pointer while scanner.scan_until(regexp)
    ary
  end
end

It’s important to remember that this will return the indexes of characters immediately after each match, for example 'something'.indexes /som|i/ returns [3, 7]. All we need to do to get the starting index for a match is to substract the length of the match string from the appropriate end index.

With these in place, we can implement our String#replace-alike. Ruby already has a String#replace method, so for want of something meaningful I’m going to call this new method js_replace.

class String
  def js_replace(regexp, &block)
    string, indexes = dup, indexes(regexp)
    n = regexp.count
    gsub(regexp) do |match|
      args = [match] + (1..n).map { |i| eval("$#{i}") } +
          [indexes.shift - match.length, string]
      yield *args
    end
  end
end

You can now pretend you’re writing JavaScript:

>> 'something'.js_replace(/(..)(m?)/) { |*args| puts args.inspect }
["som", "so", "m", 0, "something"]
["et", "et", "", 3, "something"]
["hi", "hi", "", 5, "something"]
["ng", "ng", "", 7, "something"]

>> 'something'.js_replace(/(..)(m?)/) { |a,b,c,d| d }
#=> "0357"

Announcing PackR

I’ve no idea who may have done this already, but I certainly coundn’t find anyone who had in five minutes of searching. I’ve written a Ruby port of Dean Edwards’ Packer, and released it as a Rails plugin. It includes a rake task for batch-processing the scripts in your application. Install like so:

ruby script/plugin install
    git://github.com/jcoglan/packr

More information on how to use it over here.

Why freeze when you can symlink

Just a quick thought: for a while now, I’ve been putting this in my Capistrano recipe, in the :after_update_code task:

run "cd #{release_path} && rake rails:freeze:edge TAG=rel_1-2-4"

But what’s the point in exporting the same 5Mb of files every time you deploy? Instead, let’s do this:

run "ln -s #{release_path}/../../shared/rails/1.2.4
    #{release_path}/vendor/rails"

cd into your Capitrano shared directory on your server, and run

mkdir rails
cd rails
svn export http://svn.rubyonrails.org/rails/tags/rel_1-2-4 1.2.4

Do that for as many versions as you want to keep handy, and you’re all set. Every time you deploy, your app will contain a link to one of your local copies of Rails. No more waiting around for Rails to export.

This also has the advantage that it doesn’t matter what version of Rails your host has installed, as you’re not running a Rails rake task any more. When using rails:freeze:edge, the server will try to find that task in the version of Rails specified by RAILS_GEM_VERSION in environment.rb. If you use a symlink, it doesn’t matter whether your Rails version is installed or not. The version you link to can then be used to run subsequent rake tasks.