Prerequisites
First of all, I would strongly recommend an in-depth reading on the topic of Design Patterns. Here is a good starting point. This article has other really good references on the same topic, which covers different aspects at different level of detail.
So, let's do a quick overview about design patterns. You may skip this part should you know about this already.
- What is a Design Pattern? A Design Pattern is a generalized, verified, well-tested solution to well-known problems during software development. Adapter is a popular example for solving the problem of multiple database providers (MS-SQL, MySQL, DB2, Oracle...)
- I know JavaScript and PHP, do I need to know about Design Patterns? Design Patterns are not language specific. You can apply the adapter design pattern using either PHP or Java. There are a few differences (e.g. syntax and runtime...), however, the same elements of the design pattern will appear.
- What benefits do I get? Design Pattern is about identifying common problems and the optimal solutions. A good knowledge about design patterns helps you stay in-line with the community of people like you (software engineers, front-end developers and the likes) so that you don't have to reinvent the wheels! Try it, just google for the word singleton and you will see what I mean.
A JavaScript Design Pattern - Deferred Initiation
Problem Statement
Recurring requests for costly operations initiated by external forces (usually an end-user) may cause system instability and/or resource shortage and should be avoided. Such an instability is more likely when the outcome of each operation directly affects the subsequent operation. Resources can be identified as computing power, network bandwidth...
Just imagine how many onscroll events are fired when you scroll this page down!
Example
A good example of costly operation is to perform a UI layout, especially in the context of a webpage. Sometimes we want to do a dynamic layout like keeping a sticky menu plus some animation. The new position of the menu is calculated on the scroll properties and the size of the hosting window. A quick and simple implementation is to add a callback to the scroll and resize events of the window.
function do_cool_layout() { // cool sticky code } window.onscroll = do_cool_layout; window.onresize = do_cool_layout;
This implementation works until your quality control resizes and scrolls the browser window like a maniac. If the layout computation is complex, the browser will eventually hangs or your sticky menu keeps on moving up and down after the maniac has stopped his or her moves already.
To understand the problem with this implementation, you must know that modern browsers allocate one UI thread per tab, JavaScript code is executed within this thread. Every script within the browsing tab (even timer callbacks) is executed in a nice linear way: First come, first served! So what, what's wrong with my W3C compliant ECMA5 syntactically very good looking code stops working!? And the browser hangs!
I would suggest the dear browser gets locked up in a sequence of processing the resize/scroll operations and the immediate resulting onresize/onscroll handler code. Try this piece of JavaScript in your Chrome browser (WARNING: YOU SHOULD TRY THIS CODE IN A NEW CHROME WINDOW)
To understand the problem with this implementation, you must know that modern browsers allocate one UI thread per tab, JavaScript code is executed within this thread. Every script within the browsing tab (even timer callbacks) is executed in a nice linear way: First come, first served! So what, what's wrong with my W3C compliant ECMA5 syntactically very good looking code stops working!? And the browser hangs!
I would suggest the dear browser gets locked up in a sequence of processing the resize/scroll operations and the immediate resulting onresize/onscroll handler code. Try this piece of JavaScript in your Chrome browser (WARNING: YOU SHOULD TRY THIS CODE IN A NEW CHROME WINDOW)
window.onscroll=function() { while(true) { // I am dead! The loops goes on! // This is just to simulate the effect of a long running, complicated // UI layout computation } }
Just imagine how many onscroll events are fired when you scroll this page down!
window.onscroll=function() { if (this.invocation_count) this.invocation_count++; // "this" refers to the window object else this.invocation_count = 1; console.log("Scroll count: " + this.invocation_count); }
Solution
We defer the initiation of the subsequent operation until the causative operation has actually finished. We must prevent re-entrants into the subsequent operation to guarantee that there is at most one subsequent operation at any given time in the system. In other words, the system has three distinct states:
In the above problem, we identify the UI layout computation as the subsequent operation and the scroll/resize as the causative operation.
Now you may want to do a quick copy and paste the above code into the console (preferably Chrome DevTools). Scroll as much as you like, yet, not until you stop scrolling for one second will the console print out "DOING the hard work" only once!
I have also developed a jQuery plugin which implements the idea. You can fork it here or just have a look at it. I think some of the folks here better enjoy the learn-by-example approach.
References:
http://ejohn.org/blog/how-javascript-timers-work/
![]() |
State machine showing transitions from idle to grace-period to resolving and back to idle again |
In the above problem, we identify the UI layout computation as the subsequent operation and the scroll/resize as the causative operation.
Functional Procedural Programming Example
var doing_cool_layout = false; var initiation_handler = null; function do_cool_layout() { doing_cool_layout = true; // Resolution has begun // ACTUAL LAYOUT COMPUTATION CODE console.log("DOING the hard work"); doing_cool_layout = false; // Resolution has finished } window.onscroll = function() { // Deferred handler if (doing_cool_layout) return; // Prevent re-entrance else if (initiation_handler) // Cancel the current grace-period if there's one clearTimeout(initiation_handler); initiation_handler = setTimeout(do_cool_layout, 1000); // Start the 1 second grace-period }
Now you may want to do a quick copy and paste the above code into the console (preferably Chrome DevTools). Scroll as much as you like, yet, not until you stop scrolling for one second will the console print out "DOING the hard work" only once!
I have also developed a jQuery plugin which implements the idea. You can fork it here or just have a look at it. I think some of the folks here better enjoy the learn-by-example approach.
$('#anchor').d().on('mousemove', function() { console.log('deferred-mousemove:', this); $('#mousemove-count').text(parseInt($('#mousemove-count').text()) + 1); }).on('click', function() { console.log('deferred-click:', this); $('#click-count').text(parseInt($('#click-count').text()) + 1); });
Discussion
Another example could be an online text editor which has a nice auto-save feature. The document should be saved whenever the user changes the content - by means of a keystroke or a mouse drag. You will soon run into problems if you implement an auto-save-maniac which literally saves the document upon each and every keystroke. In doing so, you are actually DOS'ing your own web server.
Another example can be found in asynchronous form validation. You cannot ask the server to validate the form immediately after each and every keystroke! In doing so, you are actually dDOS'ing your own web server plus HTTP-flooding the user's local network.
References:
http://ejohn.org/blog/how-javascript-timers-work/
This is a very good technique :D But could you please clarify what are differences between imperative programming the functional programming?
ReplyDeleteI am sorry for my word-use which was really misleading. I have changed "functional" to "procedural" as "functional" would imply a programming paradigm other than imperative paradigm.
DeleteIn this case, by "procedural" I mean a non-OOP approach: no object, no instance-scope member variables to keep track of object states.
By the way, did my code cause your browser to hang?
OK, I got it :D
DeleteI have just found this. The article talks about different programming paradigms
ReplyDeletehttp://people.cs.aau.dk/~normark/prog3-03/html/notes/paradigms_themes-paradigm-overview-section.html#paradigms_logic-paradigm-overview_title_1