Event Chaining Framework in JavaScript

Stainless-Steel-Chain

Asynchronous code flow is notoriously difficult to follow. With the deprecation of synchronous Ajax calls and increasing use of concurrent code in JavaScript, we often need to have functions execute in a step by step process via chaining.

This article presents a short solution to chaining asynchronous code in JavaScript. I wrote a small event manager in JavaScript using the principals defined in the article.

Deferred

The JQuery Deferred object acts much like promises and futures in more powerful object orient languages such as Java and Scala. A deferred object is created and used to call a function or chain of functions as needed.

var dfrd = $.Deferred();
dfrd.then(func1).then(function(result){
    console.log(result);
});
dfrd.resolve('result');

A deferred object works in exactly the same way as a JQuery Ajax call and uses the same method to handle an asynchronous method call. The created deferred can be chained using .then which allows for a result to be passed via .resolve.

A deferred object must be resolved to execute a chain. The .then method allows for not only a callback to be executed on success, but failure and progress callbacks to be executed as well.

Nesting Problem With JQuery Solutions

Nesting is messy. Unfortunately, with the JQuery solution, it is impossible to chain deferred functions without nesting them.

$.when(func1()).then(function(result1) { 
    $.when(func2(result1)).then(function(result2) {
        alert(result2);
    }) 
});

With this solution, the deferred method waits on another result before executing the next function. The call to .when allows for a promise to be utilized.

Custom Chaining Solution

While nesting is useful, it is not always the cleanest solution. Another option is to create a stack and then pop from the stack while generating a new chain.

function get_chain(step, previous_chain){
    var to = step.timeout;
    var func = step.func;
    var new_chain =  $.Deferred();
    if(to > 0){
        new_chain.then(function(result){
            return execute_step_timeout(to, result);
        })
    }
    new_chain.then(function(result){
           return func(result);
    });
    if(previous_chain){
        new_chain.then(function(result){
            previous_chain.resolve(result);
        });
    }
    return new_chain;
}

Here, a function takes a step configuration containing any deferred timeout and a previous chain and constructs a new chain.

Such a process is akin to writing the following code:

var nchain = $.Deferred()
nchain.then(function(result){
	return execute_step_timeout(1, result);
}).then(function(result){
	return func2(result);
}).then(function(result2) {
    	    alert(result2);
});

$.when(func1()).then(function(result1) {

Conclusion

Asynchronous Javascript, while not parallel, often requires chaining. This article reviewed how this can be done cleanly using the JQuery Deferred object.

 

Leave a Reply