Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Cascading Asynchronous Function Execution (CAFÉ)

5.00/5 (2 votes)
9 Aug 2014CPOL1 min read 12.8K   33  
Cascading Asynchronous Function Execution (CAFÉ) is a design pattern for easily managing the execution sequence of asynchronous requests and callbacks within a medium to large JavaScript framework.

Introduction

Cascading Asynchronous Function Execution (CAFÉ) is a design pattern for easily managing the execution sequence of asynchronous requests and callbacks within a large JavaScript framework.

Background

On a recent JavaScript framework I was building for custom web approval forms based on MVVM, the form initialization required many asynchronous requests to various web services within the company to gather data including the user’s profile, permissions, group memberships, and workflow history. The requests needed to happen in a specific order since each function relied on data from a previously executed function. All of the requests needed to execute asynchronously for optimal user experience.

After several revisions to the framework, I needed to rearrange the execution order of all of these asynchronous requests and add new ones. This quickly became a problem when tracking down the execution sequence and the callback function calls within each.

This problem compelled me to come up with a simple approach to easily rearrange the execution sequence and management of all asynchronous requests, and callbacks – all in one controlling function near the beginning of the framework code. I call this approach Cascading Asynchronous Function Execution or simply, CAFÉ.

Using the code

Create an instance of Cafe to execute your asynchronous requests as shown below.

JavaScript
//
/*
*	Cascading Asynchronous Function Execution (CAFE)
*	@params: args = {
*		fns: ['fn1, fn2, fn3, ...'],
*		statusListId: 'string'
*	} 
*/
function Cafe(args){
	this.fns = args.fns;
	this.statusList = document.getElementById(args.statusListId);	
};

Cafe.prototype = {
	
	/* handles execution of async functions in this.fns */
	cascade: function(){
		if(arguments[0]){
			this.updateStatus(arguments[0]);
		}
		
		if(this.fns.length == 0){ return; }
		
		/* execute the next function passing 'this' and any arguments from the previous function */
		this.fns.shift()(this, arguments);
	}
	
	/* simulate execution of asynchronous request for demonstration purposes */
	, ajaxSim: function(args){
		setTimeout(function(){
			args.success();
		}, 2000);
	}
	
	/* UI feedback - update the status list after each exectution  */
	, updateStatus: function(str){		
		var li = document.createElement("li");		
		li.innerHTML = str;
		this.statusList.appendChild(li);			
	}	
};

/* Run sample functions */
(function(){
	
	/* Create the CAFE instance. Pass references to async functions in array and ID of a list element */
	var cafe = new Cafe({
		fns: [fn1, fn2, fn3, fn4, fn5],
		statusListId: "StatusList"
	});
	
	/* initiate the cascade */
	cafe.cascade('Executing asynchronous cascade...');

	/* example functions - replace these functions with your own */
	function fn1(){ 
		
		//AJAX request simulator. Replace with your own AJAX code.	
		cafe.ajaxSim({
			success: function(){
				cafe.cascade("Fn1 complete."); /* callback to next function */
			}
		});
	}; 

	function fn2(){ 
		//Previous function can pass arguments to this function via the arguments property.
		var args = arguments;
	
		//AJAX request simulator. Replace with your own AJAX code.	
		cafe.ajaxSim({
			success: function(){
				cafe.cascade("Fn2 complete."); /* callback to next function */
			}
		});
	}; 

	function fn3(){ 
		
		//AJAX request simulator. Replace with your own AJAX code.	
		cafe.ajaxSim({
			success: function(){
				cafe.cascade("Fn3 complete."); /* callback to next function */
			}
		});
	};

	function fn4(){ 
		
		//AJAX request simulator. Replace with your own AJAX code.	
		cafe.ajaxSim({
			success: function(){
				cafe.cascade("Fn4 complete."); /* callback to next function */
			}
		}); 
	};

	function fn5(){  
		
		//AJAX request simulator. Replace with your own AJAX code.	
		cafe.ajaxSim({
			success: function(){
				/* callback to next function */
				cafe.cascade("Fn5 complete. All asynchronous operations complete."); 
			}
		});
	};

})();

Other Ideas for Your Implementation

Since the functions are referenced in an array, your approach could be developed further to dynamically change the order of execution or add/remove functions based on any number of circumstances in your implementation using the Array object methods including slice, reverse, sort, push, pop, and shift.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)