Reg Braithwaite's excellent post on method combinators showed how to untangle cross-cutting concerns like security, logging, and event triggering from the definitions of instance methods. The post inspired me to share another fun method combinator: asynchronous method invocation.
What is a method combinator?
A method combinator is a higher-order function which accepts a function as an argument and returns a new function which adds some extra decoration to the original. If you have a lot of code that looks like:
You can easily break out the shared, cross-cutting concern into a method combinator:
And then wrap your bare methods with the combinator:
Read Reg's post for a few more mind-binding ideas.
What is a deferred?
A deferred is an object that represents an asynchronous action, something that may or may not have happened yet, or may never happen. jQuery now includes a deferred constructor, which is quite simple to use:
The deferred combinator
We can use the behavior of deferreds to create a combinator that defer the execution of a method until later. This is handy when you have a caller who is going to call your method before you're ready to handle their request.
Let's look at an example. We have an existing class for widgets, small pieces of UI that are rendered inside of another view and aren't really necessary, they're just icing on the cake. The widget class is pretty simple:
The widget gets initiated somewhere high up in the application and then passed in to a view, which calls it when it's ready:
And we now have lots of different application views, all of which can take any of these widgets and render it, using this interface.
Now we want to create another widget, but this time the data is loaded from a third-party API. It may or may not have come back by the time the view calls our widget's render method. What to do?
Our first option is to change the widget interface to always be asynchronous. The widget can have a dataLoaded deferred object representing whether it has loaded or not and foist the complexity onto every view that wants to render it:
If there are lots of places that render widgets, that's a lot of unrelated code to change. Instead, we can contain the asynchronous behavior inside the widget itself by making the render method fire-and-forget. The caller will just call render and we'll get around to doing it as soon as we're ready:
This solution restores the original Widget contract. But notice that the render method now combines two separate concerns: figuring out when to run, and actually rendering. If the object had methods other than render that needed to wait on data, we'd have to duplicate the deferred code in each of them.
Instead, we can define a method combinator that will fire our methods asynchronously. It returns a function that will remember
And then use it to wrap methods that should wait for our deferred to be ready:
Anyone can call render whenever they want, and the body of the method will be carried outside as soon as the data is ready.
Now that we have the idea of "saving" method invocations and actually carrying them out later, we could extend it to making the invocation multiple times.
We could just write a render method that did both the rendering of the random ad and the cycling, but by separating them out we have made the code easier to test and understand.