Friday, July 27, 2012

Solving JavaScript Async Issues the Easy Way, With Events

By now we all know that JavaScript is asyncronous and what that means, right?  If not the following code will help explain the concept in 30 seconds:


This code will run function a() then b(), even if the ajax request in a() has not yet completed.

There are a lot of approaches that can be used to make sure that b only runs when a is done.  One approach would be to use jQuery deferreds like so:


We can also use a async library, such as q, if we need a bit more power.  Or we can use JavaScript's setTimeout(func, delay) function to continuously check for a variable the function a() sets when it is done before we call b(), how awful does that sound? In some cases there is a quite simple way to achieve this, by using events.  I might even argue that using events is the more JavaScripty way to do things in the right situation.  Lets take a look at the a/b example, this time using events:


Lets take take a look at a real world example of using events to make sure things happen in the proper order.  Here we will use a backbone collection as a wrapper for a one or more Contact objects that will be reused in multiple places in an application:


Let say we want to use this collection to populate a drop down when a view is initialized like so:


Using this code there is a good chance that the models will be an empty array when we try to add them to the dropdown.  Also, when the collection's fetch() completes the app.contacts.models array will be reset, meaning that the reference to the contacts in your view likely will not reference the current app.contacts.models array.

To fix this, lets first update our collection to trigger an event when the contacts are loaded.  In this example I'm going to use jQuery's trigger(eventName) function to keep things easy:


The last step is to make sure we are adding the carriers to the drop down after they have been added to the collection. To do this we just need to add an event listener to our initialize function:



Now we will always be working with a populated collection in our addContacts method.

13 comments:

  1. Just to mention that in the last examples your trigger (load) is not the same as your receiver (done).

    ReplyDelete
  2. The title of this post is misleading. This is jQuery and backbone, extensions of JavaScript. Not JavaScript.

    ReplyDelete
  3. @Donnetello, thanks for the comment and I can see your point, however, this same pattern can be reproduced without either backbone or jQuery. I just used jQuery's eventing and binding because it is much more terse than the pure JavaScript implementation. The backbone example was just a real example pulled from a project I'm currently working on, however the issue would be the same without backbone.

    @Anonymous - Thanks for the heads up, when I was writing this I had a lot of gists open and missed that detail.

    ReplyDelete
  4. "In other words it will not wait for a() to finish before it invokes b()." This is not necessarily true. Sure, if a() calls an XHR request then b() will execute before the callback for the XHR request, but a() will do its work first (fire off the XHR request) and then b() will execute. a() will always finish it's work first before b() executes in that example. The end result (whatever is happening in the callback) may make it appear that b() finished before a() but in reality that isn't what is happening.

    ReplyDelete
  5. Asynchronous style is the matter of your library(in this context, Backbone.js), not the javascript language itself.

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. There are certainly a lot of details like that to take into consideration. That is a great point to bring up.

    ReplyDelete
  8. I do consider all of the ideas you have introduced for JavaScript. It is convincing and will certainly work. You have done an excellent job on this topic!

    ReplyDelete
  9. App looks great, esp. offline feature. Too late to incorporate this for Coderace 2012, but certainly a priority for 2013.

    ReplyDelete
  10. I learned alot by just reading your post. Well thought and easy to follow. Thanks for sharing. Keep it up.

    ReplyDelete
  11. Thanks for your marvelous posting! I actually enjoyed reading it, you will be a great author.I will ensure that I bookmark your blog and will come back in the foreseeable future. I want to encourage that you continue your great job, have a nice weekend! think you've made some truly interesting points.

    National Family Solutions

    ReplyDelete
  12. I do consider all of the concepts you have presented for JavaScript. It is effective and will certainly perform. You have done an outstanding job on this subject.

    ReplyDelete
  13. First time when I came on this topic and luckily found it nice thanks for this one. This is the first time when I came on this topic and luckily found it nice thanks for this one. I must like to appreciat

    ReplyDelete

Fork me on GitHub