Halhelms
SIGN UP FOR MY NEWSLETTER

www.savorgold.com is top on wow gold and runescape gold and World of Warcraft gold provider list for trusted services. Their reputation seems to growing by the minute, which isn't surprising because they are one of the safest sellers of Gold. Delivery speed and customer service are very good. They aslo are giving some bonus items depending on the amount of gold you purchase.

 
 
Halhelms

Recent Comments

Recent Entries

RSS

Getting Started with Event Driven Programming

I was talking with a friend recently about event-driven programming. He and I have been heavily into working with custom events for a few years. We've both seen the benefits of robustness and maintainability that event-driven programs provide. Why, I asked him, have more people not adopted EDP? His answer really surprised me.

"It's because they don't know how to work with it. It's one thing to accept that it may have real benefits -- but if they don't know how to get started with it, what good does that do them?"

Wow. He's absolutely right! I've talked a fair amount about (a) the benefits of event driven programming and (b) the ideas behind event driven programming, but stopped short of explaining exactly how you might start using event driven programming without either radically changing the way you work or driving yourself crazy. So let me remedy that.

I'll start assuming that we're using jQuery. You could just as easily do EDP with almost any other JavaScript framework but jQuery is the one I'm most used to -- and it's the most popular one, by far. Let's start then, with a very simple example. And I'll show it both with and without events.

I've prepared an example page for this, that looks like this:

Click on a link, and the appropriate box will shake up and down (thank to a little jQuery animation).

Here's how you might build this without using events:

<html>
  <head>
    <script type="text/javascript" 
src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js">
</script>
  </head>
  
  <body>
    <a href="#" id="A" class="Agitater">Agitate Box A</a>
    <a href="#" id="B" class="Agitater">Agitate Box B</a>    
    <a href="#" id="C" class="Agitater">Agitate Box C</a>
    
    <div class="message" id="box_A">I am Box 'A'</div>
    
    <div class="message" id="box_B">I am Box 'B'</div>
    
    <div class="message" id="box_C">I am Box 'C'</div>
    
    <script type="text/javascript">
      $( '.Agitater' ).click( function() {
        $('#box_' + $( this ).attr( 'id' ) )
          .fadeIn(100)
          .animate({top:"-=20px"},100)
          .animate({top:"+=20px"},100)
          .animate({top:"-=20px"},100)
          .animate({top:"+=20px"},100)
          .animate({top:"-=20px"},100)
          .animate({top:"+=20px"},100);
      });
    </script>
    
    <style>
      .message{
        border:1px solid #CCCCCC;
        padding: 15px;
        width: 150px;
        background-color:#F4F4F8;
        text-align:justify;
        position:relative;
        top:50px;
        display: inline;
        margin: 1.75px;
      }
      
      a{
        display : block;
      }
    </style>
  </body>
</html>

Now, in fact, I am using events -- DOM events. The jQuery function listens for a "click" event and responds to it. When I'm referring to event driven programming, though, I'm referring to what jQuery calls custom events -- events not propagated by the DOM but by you, the programmer.

Now, here's the same page, but this time using a custom event -- called "agitation_request".

<html>
  <head>
    <script type="text/javascript" 
      src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js">
    </script>
  </head>
  
  <body>
    <a href="#" id="A" class="Agitater">Agitate Box A</a>
    <a href="#" id="B" class="Agitater">Agitate Box B</a>    
    <a href="#" id="C" class="Agitater">Agitate Box C</a>
    
    <div class="message" id="box_A">I am Box 'A'</div>
    
    <div class="message" id="box_B">I am Box 'B'</div>
    
    <div class="message" id="box_C">I am Box 'C'</div>
    
    <script type="text/javascript">
      $( '.Agitater' ).click( function() {
        $( 'body' ).trigger(
          'agitation_request',
          {
            box : $( this ).attr( 'id' )
          }
        );  
      } );
      
      // Listeners
      $( 'body' ).bind( 
        'agitation_request', 
        function( eventName, eventObject ){
            $( '#box_' + eventObject.box )
              .fadeIn(100)
              .animate({top:"-=20px"},100)
              .animate({top:"+=20px"},100)
              .animate({top:"-=20px"},100)
              .animate({top:"+=20px"},100)
              .animate({top:"-=20px"},100)
              .animate({top:"+=20px"},100);
      } );
    </script>
      .message{
        border:1px solid #CCCCCC;
        padding: 15px;
        width: 150px;
        background-color:#F4F4F8;
        text-align:justify;
        position:relative;
        top:50px;
        display: inline;
        margin: 1.75px;
      }
      
      a{
        display : block;
      }
    </style>
  </body>
</html>

This code still listens for the DOM "click" event but this time, instead of reacting to it directly, it announces a custom event using this code:

$( '.Agitater' ).click( function() {
  $( 'body' ).trigger(
    'agitation_request',
    {
      box : $( this ).attr( 'id' )
    }
  );  
} );

In a future post, we'll delve into the specifics of the jQuery trigger method. For now, just know that this is used to spawn a new custom event and that when it does so, it passes both the eventName ("agitation_request") and an eventObject with key/value pairs.

So, we have a new custom event being fired. Is anyone listening? Yes, as shown here:

// Listeners
$( 'body' ).bind( 
  'agitation_request', 
  function( eventName, eventObject ){
      $( '#box_' + eventObject.box )
        .fadeIn(100)
        .animate({top:"-=20px"},100)
        .animate({top:"+=20px"},100)
        .animate({top:"-=20px"},100)
        .animate({top:"+=20px"},100)
        .animate({top:"-=20px"},100)
        .animate({top:"+=20px"},100);
} );

For this simple example, you may think "That event stuff is just more code." And it is, of course. But follow this series of posts on event driven programming and you may change your mind. For now, though, let's introduce one very small wrinkle to our scenario.

The Boss, having seen this program, has a request.

"Doggone it! I had no idea you could do that. Is that HTML-5? No? HTML Six? Well, whatever it is, I love it! Keep up the good work. Oh, one more thing -- could you just keep a list of when people click one of those? Our Marketing Department says this is invaluable information."

This isn't a big request. In our non-eventful code, we can just add a bit to our DOM listening code:

<script type="text/javascript">
  $( '.Agitater' ).click( function() {
    $('#box_' + $( this )
      .attr( 'id' ) )
      .fadeIn(100)
      .animate({top:"-=20px"},100)
      .animate({top:"+=20px"},100)
      .animate({top:"-=20px"},100)
      .animate({top:"+=20px"},100)
      .animate({top:"-=20px"},100)
      .animate({top:"+=20px"},100);
    // new code
    $( '#clickers' ).append( $( '<li>' + 
    $( this ).attr( 'id' ) + ' was clicked.</li>'));
  });
</script>

And after a few clicks, the Boss sees something like this:

But some coders will recognize that we committed something of an indiscretion: we changed code that was written, tested, and working. Do enough of that and you'll have a very difficult application to maintain.

The alternative -- in our eventful code -- might be adding some code like this:

// Listeners
$( 'body' ).bind( 'agitation_request', function( eventName, eventObject ){
  $( '#box_' + eventObject.box )
    .fadeIn(100)
    .animate({top:"-=20px"},100)
    .animate({top:"+=20px"},100)
    .animate({top:"-=20px"},100)
    .animate({top:"+=20px"},100)
    .animate({top:"-=20px"},100)
    .animate({top:"+=20px"},100);
} );

$( 'body' ).bind( 'agitation_request', function( eventName, eventObject) { $( '#clickers' ).append( '<li>' + eventObject.box + ' was clicked.</li>' ); });

The original written, tested, and working code is left unchanged. But now, I've added another listener, listening for the same event -- and reacting differently. This seemingly small change is going to have very, very large benefits.

Stay tuned...

You can find the sample code at http://github.com/halhelms/Blog-Post-GSWEDP

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Ben Nadel's Gravatar Sounds like a good series to me :) I'll be watching.
# Posted By Ben Nadel | 10/24/10 11:25 PM
Attila Domokos's Gravatar I think it's great how you decoupled the event publishing and handling mechanism! Nice job, I'll be back for more. One question: how would you write unit tests against this code? I'd like to see that!
# Posted By Attila Domokos | 10/25/10 1:09 PM
Hal Helms's Gravatar @Attila In future posts, I'll show how the listeners will all be placed into their own files. From there, one can write unit tests. I use Jasmine because it's very easy to use and very similar to RSpec in Rails.
# Posted By Hal Helms | 10/25/10 3:14 PM
 
   
Clicky Web Analytics