Overcoming Asynchronous Anxiety By Testing JavaScript's Event Loop

Posted April 27, 2012 at 9:48 AM by Ben Nadel

Tags: Javascript / DHTML

JavaScript runs on an event loop. I know this. I've tested this before. I understand race conditions and intervals. And yet, I still find myself harboring anxiety when it comes to asynchronous JavaScript actions in the browser. My latest unfounded and irrational fear comes from monitoring the location Hash of the browser. Specifically, keeping the location hash in sync with an internal hash representation.


 
 
 

 
  
 
 
 

When I build a JavaScript application that reacts to changes in the window location's Hash value, I keep an internal representation of the hash that I use as a control against which to compare the ongoing location hash. The monitoring of the location hash takes place in some sort of interval; and, when the location hash and the internal hash fall out of sync, I know that my location hash has changed. And, I can react to that change as necessary.

If I want to update the location hash manually - that is, I want my application to manipulate the location hash - I'll usually do something like this:

  1. Turn OFF location hash monitoring.
  2. Set internal hash.
  3. Set location hash.
  4. Turn ON location hash monitoring.

I turn off the location hash monitoring while updating the internal hash because I am worried that my monitoring interval will catch me in between steps 2 and 3: where I have updated the internal hash, but have not yet updated the location hash. I am worried that this will inaccurately detect a change in the location hash.

This fear is irrational.

This scenario does not exist.

The JavaScript event loop will not allow it.

I know this rationally; but, as I just said, the fear is irrational. To try and cure myself of the fear, I put together a quick demo that tries to put my fear to the test. In the following code, we're going to monitor the location hash. And, we're going to update the internal hash and the location has without pausing the monitor. And, we're going to try to waste a lot of time in between those two steps.

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Understanding JavaScript Event Loops And Timeouts</title>
  • </head>
  • <body>
  •  
  • <h1>
  • Understanding JavaScript Event Loops And Timeouts
  • </h1>
  •  
  • <p>
  • <a href="#" class="hashTrigger">Change Hash</a>
  • </p>
  •  
  • <p class="text">
  • This is just some text in a DOM element that I will be
  • manipulating in order to provide a process-heavy way
  • of killing some time. Yeahhhhh booooyyyyyyy!
  • </p>
  •  
  •  
  • <!-- Initialize scripts. -->
  • <script type="text/javascript" src="../jquery-1.7.1.js"></script>
  • <script type="text/javascript">
  •  
  •  
  • // Get and cache our DOM elements.
  • var dom = {
  • trigger: $( "a.hashTrigger" ),
  • text: $( "p.text" )
  • };
  •  
  • // Keep track of our current hash value.
  • var currentHash = location.hash;
  •  
  •  
  • // I update the internal hash and the window hash. In between
  • // those two updates, however, I kill time to see if the
  • // browser's event loop will notice the discrepency.
  • function setHash( newHash ){
  •  
  • // First, we'll set the internal hash value so that
  • // when the location hash changes, it changes INTO
  • // sync with the internal hash (not OUT OF sync).
  • currentHash = ("#" + newHash);
  •  
  • // Waste time updating the DOM. We're doing this to see
  • // if our interval for location-checking will see the
  • // internal hash change before we change the window's
  • // hash value.
  • wasteTime();
  •  
  • // Update the location.
  • location.hash = newHash;
  •  
  • };
  •  
  • // I check to see if the window hash and the internal hash
  • // are out of sync.
  • function checkHash(){
  •  
  • // Make sure they match.
  • if (currentHash !== location.hash){
  •  
  • console.log(
  • "Hash out of sync:",
  • currentHash,
  • location.hash
  • );
  •  
  • }
  •  
  • }
  •  
  • // I try to waste time by updating the DOM and causing
  • // re-rending of the page.
  • function wasteTime(){
  •  
  • // Increase font size.
  • for (var i = 1 ; i < 10000 ; i++){
  •  
  • dom.text.css( "font-size", (i + "px") );
  •  
  • }
  •  
  • // Decreate font-size.
  • for (var i = 10000 ; i > 20 ; i--){
  •  
  • dom.text.css( "font-size", (i + "px") );
  •  
  • }
  •  
  • }
  •  
  •  
  • // Set up the hash trigger to always change the hash to the
  • // current timestamp.
  • dom.trigger.click(
  • function( event ){
  •  
  • // Kill the default event - this isn't a real link.
  • event.preventDefault();
  •  
  • // Set the hash to the epoch time.
  • setHash( (new Date()).getTime() );
  •  
  • }
  • );
  •  
  • // Start monitoring the hash change.
  • setInterval( checkHash, 10 );
  •  
  •  
  • </script>
  •  
  • </body>
  • </html>

If you look in my setHash() function, you'll see that it calls the method, wasteTime(), in between setting the internal hash representation and changing the location hash. This wasteTime() method runs a bunch of process-intense DOM (Document Object Model) updates that cause the browser to redraw (the slowest thing you can do in JavaScript). And still, even with this very noticeable delay in processing, the location monitoring never finds the location hash out of sync.

NOTE: You can see this in the video.

This is because JavaScript's event loop won't execute the next tick of the monitor interval until all the synchronous processes of the current tick are done. This means that even if my current tick is doing a lot of work, including updating both hashes (internal and external), it will successfully complete before the monitor interval is allowed to execute.

Hopefully this exploration will be enough immersion therapy to put my event loop anxiety to rest.




Reader Comments

Apr 30, 2012 at 11:01 PM // reply »
1 Comments

That was a short, but highly informative video. In fact, I've often steered clear of setInterval and setTimeout for just this reason. So if I can extend the argument logically, does this mean that setInterval waits for its bound function to be the only function on the call stack before executing the next iteration?


Aug 3, 2012 at 2:26 PM // reply »
2 Comments

great post, this should help those of us who suffer irrational eventusansaphobia (fear of the event loop). personally, just knowing that the complete execution of the handler takes precedence over the clock puts my mind almost 100% at ease. almost.


Aug 3, 2012 at 2:57 PM // reply »
11,238 Comments

@Mark,

I can't say that I know how things get chosen to run; but, what I can say is that when the callback in setTimeout() / setInterval() is running, you can be *sure* that it's not conflicting with code outside of the callback.

@Michael,

Thanks! Hoping to put all of our minds at ease :D


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools