Ask a Jedi: CFAJAXPROXY and Managing Callbacks

This post is more than 2 years old.

Yaron asks:

I'd like to know what your preference is for using cfajaxproxy. In JavaScript, do you create one global proxy object and reuse it throughout your script? Or do you create a new proxy object within every function that generates a proxy call?

The reason I'm asking is I had multiple concurrent proxy calls that had their callback functions mixed up. Meaning, one functions makes 2 async calls with two separately defined callback functions. Unfortunately, one callback function received the input from another. Weird.

Ah, asynchronous network calls. Life would be a heck of a lot easier if everything was synchronous. Let's dig a bit into what Yaron found in case it doesn't make sense.

First, consider the following CFC that we will use for our Ajax calls:

<cfcomponent output="false" extends="base">

<cffunction name="goSlow" access="remote" returnType="string"> <cfargument name="name" type="string" required="true"> <cfset sleep(300 * randRange(1,4))> <cfif arguments.name is "foo"> <cfset sleep(200 * randRange(1,4))> </cfif> <cfreturn "Returned from call #arguments.name#"> </cffunction>

</cfcomponent>

It has one method, goSlow, that runs a randomly slow sleep call, and makes it even longer if foo is passed as an argument. It then returns the argument passed to it.

The front end code for testing will be:

<cfajaxproxy cfc="test" jsclassname="testProxy"> <script> var foo = new testProxy() var goo = new testProxy()

function handleResult(r) { console.log(r) }

foo.setCallbackHandler(handleResult) goo.setCallbackHandler(handleResult)

function runTest() { console.log("Running test....") foo.goSlow('foo') goo.goSlow('goo') console.log('Done with tests') } </script>

<input type="button" onClick="runTest()" value="Test">

This page makes use of cfajaxproxy to create a proxy calls called testProxy. I created two instances of it and assigned the same callback handler. The callback handler gets the result, but really has no idea who calls it. This is critical. Unless you set up some mechanism to pass in a 'caller' value, then you can't tell what you are responding too. Not only can't we tell which instance of testProxy was used, we can't even tell what method was called.

So given that - what are some good ways to handle it? You could create a different call back handler for each instance. You can even do this inline:

foo.setCallbackHandler(handleResult) goo.setCallbackHandler(function(r) { console.log('special '+r)})

This kinda surprised me. I mean I know that this type of function (an anonymous function) isn't jQuery only, but I didn't start using it till I got big into jQuery. Still though, if you want to run N different methods on the proxy CFC, do you really want N different instances?

My guess is probably yes. Given that you may have one main "service" CFC to handle your Ajax calls, you could create different instances for different areas of concern. So for example:

var userProxy = new testProxy() var pageProxy = new testProxy() var cowbellProxy = new testProxy()

Each proxy in the above code sample will worry about different aspects of remote CFC service.

Can anyone else offer some advice here?

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Azadi Saryev posted on 6/30/2009 at 2:21 PM

A very similar question was asked not long ago in CF area of Experts Exchange. and the answer was the same: if there is a chance that one of your remote calls may 'stick' for a long time - create separate js proxy instances for your functions.

what i also usually do, is create a separate proxy for GET calls and POST calls. otherwise after you set request format in one of your function calls to POST, all your other remote calls will keep using POST method (unless you set a method in each and every ajax request), totally screwing up returned data formats...

Azadi

Comment 2 by Andy Sandefer posted on 6/30/2009 at 3:10 PM

I have done extensive work with cfajaxproxy and I typically create more than one instance - for instance (stupid pun intended)...
What if I have a cfc named item.cfc and item.cfc contains the methods for deleting an item and checking to see if an item already exists, which I'll call before I try to do a ColdFusion.Ajax.submitForm with the form data used to create or update a new inventory item. My code would probably look something like this (this is pseudo but if someone wants to see true examples that show how to chain functions, etc. I'll post it)...

<cfajaxproxy cfc="cfc.item" jsclassname="proxyItem">

deleteItem = function(){
/*Code to grab the item number they want to delete and probably a js confirm to see if they're serious about deleting the item - if the confirm returns a true then we can call the remote deleteItem function which would check to see if we could delete an item (we don't want to allow deletes if an item has been used on a transaction in our system because that would break our referential integrity)
The cfc will return a boolean telling us whether or not we were able to delete the item*/
var itemID = ColdFusion.getElementValue('itemID', 'editItem', 'value');
var proxyDeleteItem = new proxyItem();
var proxyDeleteItem.setCallbackHandler(deleteItemCallback);
//Error Handler and Caller
}

deleteItemCallback = function(itemDeleted){
//function code here
/*
So if the cfc passes back a true we would tell the user that their delete was a success and we would route them to a new page, like say the item list page or something like that
*/
}

checkForItem = function(){
//function code here will check to see if an item exists
var itemID = ColdFusion.getElementValue('itemID', 'editItem', 'value');
var proxyCheckItem = new proxyItem();
var proxyCheckItem.setCallbackHandler(deleteItemCallback);
//Error Handler and Caller would follow here
}

checkForItemCallback = function(itemExists){
/*If the item exists then flash an alert to let the user know that they need to specify a unique inventory item number, otherwise call the submitForm function, which you guessed it - probably has a callback of it's own!
*/
}

submitForm = function(){
}

submitFormCallback = function(){
}

If you're looking for shortcuts and less typing then you won't find it - about the only thing that I reuse is an error handler.
BTW, @Ray already knows this so I'm not sure why he did it (probably lazy genius syndrome crap) but anyway I'll throw in my two cents in saying that structuring js functions in the manner of
function MyFunctionName(){
}
causes nothing but problems. I know that this is just example code that he's posted but if you're truly going to do a lot of work with remote calls via js they should all be conjured up using
MyFunctionName = function(){
}

Comment 3 by Raymond Camden posted on 6/30/2009 at 3:32 PM

@Azadi: I'm a bit confused by your very last statement. Whether or not you use GET or POST has no impact on the return format. You can return any format to either a GET or POST request. Have you seen otherwise?

Comment 4 by Raymond Camden posted on 6/30/2009 at 3:35 PM

@Andy: I disagree about function x versus x = function. As far as I know, function x format only is a problem when the code is being loaded in via Ajax. When it is the top level code, it runs just fine.

Comment 5 by Yaron posted on 6/30/2009 at 3:57 PM

Ray,
Thanks for posting this. I'm always concerned about using just the right amount of resources (even if it's the client browswer's). When my discovery first came to light I switched to a new instance per call. I'm curious, does anyone know, is there a heavy tax on browser resources for each instance?
It would be nice if Adobe created an ASYNC management system (perhaps an array of callback functions) that track remote calls to their assigned call back functions, insuring these problems will never occur.
Thanks,
Yaron

Comment 6 by Raymond Camden posted on 6/30/2009 at 4:06 PM

@Yaron: I've never thought much about JS usage and memory. I've done some basic reading on stuff in the past - I know IE has some issues with garbage collection, but I can't remember offhand the particulars. I also know that JavaScript has the same issues with string concats that Java does. But as for making objects - I'd assume that as long as it's reasonable, then it's nothing to worry about. If you are making _hundreds_ of objects, then I'd worry about memory. That may actually be something to worry about if your page is Gmail-like, in other words, a RIA that a user may stay at and never reload.

Comment 7 by Andy Sandefer posted on 6/30/2009 at 7:41 PM

@Ray - I'm not trying to be a jerk but what is the advantage of

function functionName (arguments) {function body}

over using

functionName = function(arguments) {function body}

The content of both functions is compiled when they're declared in this manner and the second syntax model allows for a function to be a property of an object. In JavaScript a function is most definitely an object. I'll take the syntax that works 100% of the time and I'll take consistency in my code until someone can illustrate a valid reason as to why I shouldn't handle function declarations in this manner.

Comment 8 by Raymond Camden posted on 6/30/2009 at 10:54 PM

Jerk! Sorry, had to say it.

Um, well, I guess that outside of callbacks in jQuery I don't normally use anonymous functions, so if I know I'm writing in a 'parent' template I just use function x(). I'm not married to it. ;)

Comment 9 by Andy Sandefer posted on 6/30/2009 at 10:56 PM

LOL