Friday, July 4, 2014

Caching Autocomplete Responses Using Lodash

I've taken a few approaches to caching responses in the jQuery UI autocomplete widget. A response, in autocomplete parlance, is the data that's displayed when the user is typing in the text input. Each keystroke is a request for data — the response is something the autocomplete must generate by filtering some data source. The filtering operations can be done locally, or remotely. Either way, we stand to benefit from caching these responses.

All my previous attempts at caching autocomplete responses had their shortcomings. For example, it was difficult to implement a caching mechanism that worked equally well for both local and remote data sources. This new approach I'm using isn't without it's faults. It utilizes the Lodash library, which isn't necessarily a good thing if you're not already using it. Here's the example I came up with.

$.widget( 'app.autocomplete', $.ui.autocomplete, {
        
    terms: {},

    _initSource: function() {
        this._super();
        this.source = _.wrap( this.source, this.cache );
    },
        
    cache: function( source, req, res ) {
        _.has( this.terms, req.term ) ?
            res( this.terms[ req.term ] ) :
            source( req, res );
    },
        
    __response: function( content ) {
        if ( !_.has( this.terms, this.term ) ) {
            this.terms[ this.term ] = content;  
        }
        this._super( content );
    }

});

In this simple extension of the autocomplete widget, we're overriding a couple methods, and providing a new one. In addition to the new terms property, serving as the cache itself. The _initSource() method is private, and called automatically by the widget to make sure there's a data source that's ready to use. We call the original implementation so that the source() method can be setup. Then we use wrap() to wrap the cache() method around source(). This is where we check for the existence of a cache key.

Next, we have the cache() method itself, which is wrapped around the source() method. It uses the has() to check for the existence of a key, and if it finds one, it can call the response directly, using the cached value. Otherwise, we have to call the source() implementation.

Lastly, the __response() method is always called, no matter what the autocomplete source looks like — array, remove, whatever. So this is a good spot to actually cache the response values based on the current search term. The whole approach works equally well for any data source and Lodash makes easy work of it.