Enhanced Javascript Lazy Function Definition Pattern

getting lazy in javascript The lazy function definition pattern is pretty simple at heart: you have a function which does some work, caches the results through a closure variable, and then overwrites itself with a new function that simply returns the closure variable. So the first time you run a function will be the only time the function does any work. Each subsequent call is “lazy” and just returns the results of that first call.

This is all fine and dandy, but what if you want to conditionally reset your lazy function?

Functions that work smarter, not harder

Consider this: you have a page with several related but independent widgets. The page is often redrawn as you work on the data, forms, maps, etc. You have to redraw the widgets often even if the state of a particular widget hasn’t changed. By using the lazy function pattern you can easily do this by having the function rendering the widget cache itself. But what happens when the widget’s state is changed? How do you easily reset it and rebuild from scratch?

Sending lazy functions back to work

Here is my test enhancement to make a reset possible:

var curState = "Washington";
var stateInfo = function ()
{
var reset = stateInfo;
var pState = curState;
var theInfo = "The state is " + curState + ".";
var showInfo = function ()
{
return (pState == curState) ? theInfo : reset();
};
stateInfo = showInfo;
return stateInfo();
};

Proof in the pudding

So the first time we call stateInfo() the function does a bunch of work, creates the results we want, and returns the following:

> The state is Washington.

The second time we call stateInfo() the function just does a variable check and then returns the cached copy of the initial results (as in the showInfo() function above):

> The state is Washington.

But what if we change the State?

curState = 'New York';

stateInfo();

> The state is New York.

On this call of stateInfo() the function ends up invoking reset() which is a copy of our original function. Thus it recreates itself and re-caches the new results. Cool eh?

But what if we go back to Washington?

curState = 'Washington';

stateInfo();

> The state is Washington.

What we have here is a ladder of cached results. The reset() function is called by our current stateInfo() function because the state is not New York. But reset is just a copy of the earlier stateInfo() which contains a cached copy of the results for Washington. So that is displayed. If we change the state to something new we will again climb the ladder until we reach the original copy of the reset() function which will produce our results for the new state.

curState = 'Montana';

stateInfo();

> The state is Montana.

How it works

Let’s walk through the code.

  1. We define curState as a global variable that holds the current US State.
  2. We define stateInfo() as a function to show information about the state.
  3. Inside stateInfo() is where things get interesting:
    1. We start by grabbing a copy of the current stateInfo() function and saving it as a private variable called reset(). Closure will hold onto this function from here on out. After the first run of the code, reset() always points to the previous instance of stateInfo() all the way up to the very first instance which has the logic to build the display again.
    2. Then we do our work (in this case just a string concatenation, but this could be an Ajax call, form validation, etc).
    3. Then we define the showInfo() function which will be our lazy function in just a half second.
    4. Inside the showInfo() function we do a ternary check to see if the pState variable is the same as the global curState variable. If so, we return our results. Otherwise we call reset().
    5. After this all happens we replace the global function stateInfo() with our new showInfo() function.
    6. Finally we call return the results of a call to stateInfo().

Encapsulation and a dirt simple API

This model allows you to easily expose an optimized and extremely simple API for your application. In one single function you roll the “work” your code needs to do, the logic to decide when to do your work, and a cache of the each instance of your rendered results. For your application logic you only ever have to call doSomething() and doSomething() will either find the cached copy or create a new set of results. But in either case it will always do the correct thing.

One Response to “Enhanced Javascript Lazy Function Definition Pattern”

1

[…] Enhanced Javascript Lazy Function Definition Pattern […]