Monthly Archives: April 2016

Cached Data Library

In my previous post I described a technique for storing the results of Ajax calls in the browser and looking them up before making subsequent calls. This way unneeded Ajax calls can be prevented and a huge performance gain can be achieved. This is especially effective for Coach Views that are used inside of Tables in IBM Business Process Manager.

Since I am lazy and don’t want to think about how to actually implement this technique every time I want to make an Ajax call in a custom Coach View, I prefer to keep functions like these in a utility JavaScript library that handles everything for me. This could look like this:

var cachedData = (function(){
	// Create a unique string identifier from the ajax service's url and the given params
	var _createAjaxId = function(fnAjax,params){
		var url = fnAjax.toString().replace(/[^]*url\s=\s\"(.*)\"[^]*/,"$1"),
			serviceId = url.replace(/.*service\/(.*)\?.*/,"$1").replace(/-/g,'_').replace(/[^\w]/g,"");
			paramid = JSON.stringify(params).replace(/\s*"[^"]+"\s*:/g,"").replace(/[^\w]/g,"");
		return 'ajx'+ serviceId + paramid;
	},
	
	// Creates a shared object for the given properties:
	_createShared = function(p_sName){
		cachedData._shared = cachedData._shared||{};
		cachedData._shared[p_sName] = cachedData._shared[p_sName]||{ ready: false, loading: false};
		return cachedData._shared[p_sName];	
	},

	// Returns the globally shared object. If it cannot be found, it is created.
	_getSharedObj = function(p_sName){
		try{ return cachedData._shared[p_sName]||_createShared(p_sName); }
		catch(e){ return _createShared(p_sName);}
	},

	// Returns the content of the globally shared object:
	getShared = function(p_sName){
		return _getSharedObj(p_sName).content;
	},

	// Sets the shared object and executes all registered handler functions:
	setShared = function(sName,oData){
		if(oData){
			_getSharedObj(sName).content = oData;
			_getSharedObj(sName).ready = true;
			
			var handlers = _getSharedObj(sName).onloadhandlers, i;
			for(i=0; i<handlers.length; i++){
				 if(typeof handlers[i] == "function")handlers[i](oData);
			}
		}
		_getSharedObj(sName).loading = false;	
	},
	
	// The main function which wraps the ajax call:
	ajaxCached = function(props){
		if(!props.load||!props.context)return;
		var fnAfterLoad = props.load,
			sServiceName = props.serviceName,
			oContext = props.context,
			params = props.params,
			sName = _createAjaxId(oContext.options[sServiceName],params);

		// Load data and store in shared object:
		if(_getSharedObj(sName).ready){
			fnAfterLoad(getShared(sName));
		}
		else {
			_getSharedObj(sName).onloadhandlers = _getSharedObj(sName).onloadhandlers || [];
			_getSharedObj(sName).onloadhandlers[_getSharedObj(sName).onloadhandlers.length] = fnAfterLoad;
			if(!_getSharedObj(sName).loading){
				oContext.options[sServiceName]({
					params: JSON.stringify(params),
					load:function(data){
						setShared(sName,data);
					}
				});
				_getSharedObj(sName).loading = true;
			}
		}
	};

	// Expose public methods:
	return {
		get : getShared,
		set : setShared,
		ajax : ajaxCached
	}
})();

What it does is provide a wrapper function for the Ajax call. All that needs to be done to get this to work is upload it to your project (Process App or Toolkit), link to it in the Coach View where you make your Ajax call, and then make the call in the following fashion:

var _this = this;
cachedData.ajax({
	context: _this.context,
	serviceName: "sampleAjaxService",
	params: {text: "sampleInput"},
	
	// Callback after shared data is loaded:
	load: function(data){
		console.log(data);
	}
});

Adjust sampleAjaxService and params to your specific use case and voilĂ : Your Ajax calls’ results will be cached and unneccessary calls will be prevented using the cached data instead.

Note that in order to store the results a unique key is created from the Ajax service’s name (based on its URL, not the name of the configuration option as that is not unique) and any parameters that were submitted via the params property. This means that the same Ajax service only uses the cache if the parameter configuration is the same as from a previous call.