Sometimes a function you want to execute may have some internal variable that has yet to be loaded. The Deferment Pattern requires that you add a little extra code to the function, but allows you to call it at any time, forcing the function to wait until the internal variables are ready. For Mint.com I have developed an on-demand Dialog system that uses AJAX to retrieve the Dialog DOMs as needed. However, I often need to access the object immediately after instantiation, which is long before the server has returned the DOM. For situations like this, I find that, lately I have been leveraging the Deferment Pattern.
For example, suppose you have an alert Dialog that you want to retrieve from the server:
Example 1: Calling Dialog Manager
var dialog = Core.Widget.DialogManager.get(alert);
Assume that DialogManager is somewhere in your library and it initiates an AJAX request to retrieve the Dialog DOM, then returns a Dialog object. Somewhere in DialogManager this Dialog object will be cached and the DOM loaded appropriately when the AJAX request is returned. But in the the meantime you have the dialog
variable and you want to start manipulating it right away. Say, the Dialog object has a method show
, which makes the Dialog visible, and you want to call that function immediately:
Example 2: Showing the Dialog
var dialog = Core.Widget.DialogManager.get(alert); dialog.show();
Without the Deferment Pattern this would most likely crash (or do nothing), because the Dialog DOM is empty, as the AJAX request most likely has not returned yet. Lets assume that when the AJAX request returns, the Object
dom is attached to the Dialog object and that this object contains a reference to the HTML element for the Dialog DOM associated to the
body key (dom = {body: element}). Therefore, the
show method might be as simple as:
Example 3: Basic Show
dialog.show = function() { this.dom.body.style.display = block; };
Obviously, this function would fall into the pitfall mentioned above. Therefore, we add the Deferment Pattern, to force the function to wait until dom.body is ready.
Example 4: Show Using Deferment Pattern
dialog.show = function() { var that = this; // deferment block; if the node doesnt exist yet, wait for it if (! (this.dom && this.dom.body)) { setTimeout(function() {that.show.call(that);}, 250); return; } this.dom.body.style.display =block; };
In Example 4, the show
Function tests to see if the dom
and dom.body
variables are set. When they are not, the function re-calls itself in a quarter of a second, using the native setTimeout
method. Scope is maintained by using the call
Function of the show
Function and passing in the scope of the original show
Function. It will keep defering indefinitely, until the DOM objects are set, then it will execute the normal operation of the show
Function.
This a great technique to use in conjunction with Lazy Function Definition Pattern, so that once the deferment is complete, you wont need to execute that check again. This can be a performance boost if your
if statement operation is more complex than a simple existance test. You may also find it useful to send parameters into the function you are defering. Here is an example using the Lazy Function Definition Pattern and extra parameters:
Example 5: Show Improved
dialog.show = function(display) { var that = this; // deferment block; if the node doesnt exist yet, wait for it if (! (this.dom && this.dom.body)) { // notice the parameterdisplayis passed here setTimeout(function() {that.show.call(that, display);}, 250); return; } // Lazy Definition Pattern this.show = function(display) { this.dom.body.style.display = display; }; this.show(display); };