Tuesday, May 15, 2012

Advanced Asynchronus JavaScript Function Call With Event Subscriptions

Per a request for my good friend, Adam Hew, who enjoyed my last post, I've thrown together a quick implementation of an asynchronous function caller with JQuery ajax-esque event subscriptions. 1.7+ of course. ;).  The challenge here was to put together something where multiple subscribers can be notified when the code is complete or if it errors or if it's successful. So here goes:

My Implemention Of An Async Caller With Event Subscription

var async = (function() {
    var AsyncResult = function(caller) {
        // set up some events.
        var completeEvents = [];
        var successEvents = [];
        var errorEvents = [];
        // and some event triggers.
        var onComplete = function() {
            for (var i = 0; i < completeEvents.length; i++) {
        var onSuccess = function(result) {
            for (var i = 0; i < successEvents.length; i++) {
        var onError = function(err) {
            for (var i = 0; i < errorEvents.length; i++) {
        //a little prep work.
        var self = this;
        this.caller = caller;
        //set up our subscription methods.
        this.complete = function(fn) {
            return self;
        this.success = function(fn) {
            return self;
        this.error = function(fn) {
            return self;
        //call the function async and kick off triggers.
        setTimeout(function() {
            try {
            } catch (err) {
        }, 0);
    return function(fn) {
        return new AsyncResult(fn);

//Now try it out!
async(function() {
    return 2 + 2;
}).complete(function() {
}).success(function(result) {
    console.log('result is ', result);
}).error(function(err) {
    console.log('ERROR!', err);

//or another way (showing multiple subscriptions):
var r = async(function() {
    return 'foo';
r.success(function(data) {
r.success(function(data) {
    alert(data); //alert it too? sure.

So what's going on here? First thing is first, I needed to figure out what to do. Well to follow JQuery's pattern, I had to have an object to return from my method that had subscription methods built into it. And those subscription methods need to return a reference to their owner... so I needed to create the AsyncResult class. But, I didn't want my consumers just creating instances of that class willy-nilly, so I needed to wrap that in some closure to hide it from all but the code that I want to create new instances of it. So I wrapped the class entirely in a one-time called function that returns a reference to another function that does the work of creating the class. Now, inside the class, I did a very simple event pattern. Basically I added some arrays for each event to hold handlers for those events. Then I added some triggers (onComplete, for example) that could be called to loop through the handlers in those arrays when they were called. Lastly, I added some subscription methods that would essentially just push handlers onto those arrays. Then I was pretty much done except for adding the setTimeout trickery to kick off the passed function in an asynchronous fashion. Lastly, I added the "caller" property to AsyncResult as a nice-to-have so a consumer could get the instance of the function that was passed to the async method.

I hope that's all clear enough. I feel like I muddled my way through that explanation a little.

EDIT: I failed to mention one important thing, setTimeout will wait for any current block of code to complete before it will actually fire! So, if you have a setTimeout in a function with some huge loop that takes forever, that setTimeout, even if set to an interval of zero, will not fire until the executing code finishes, loop and all.

No comments:

Post a Comment

This form allows some basic HTML. It will only create links if you wrap the URL in an anchor tag (Sorry, it's the Blogger default)