How to refactor this Javascript anonymous function? - javascript

We have this anonymous function in our code, which is part of the jQuery's Ajax object parameters and which uses some variables from the function it is called from.
this.invoke = function(method, data, callback, error, bare) {
$.ajax({
success: function(res) {
if (!callback) return;
var result = "";
if (res != null && res.length != 0)
var result = JSON2.parse(res);
if (bare)
{ callback(result); return; }
for (var property in result) {
callback(result[property]);
break;
}
}
});
}
I have omitted the extra code, but you get the idea. The code works perfectly fine, but it leaks 4 Kbs on each call in IE, so I want to refactor it to turn the anonymous function into a named one, like this.onSuccess = function(res) { .. }.
The problem is that this function uses variables from this.invoke(..), so I cannot just take it outside of its body. How do I correctly refactor this code, so that it does not use anonymous functions and parent function variables?
Update. I am thinking of creating a separate object, initializing it with the same parameters, and pass its onSuccess function as a parameter for jQuery's Ajax object. Although I suspect that it will still leak memory.
Update 2. I have found a few links suggesting that the actual leak might be caused by jQuery.
Simple jQuery Ajax call leaks memory in Internet Explorer
Memory leak involving jQuery Ajax requests
Still it was good to find a way to refactor this.
Update 3. I will wait for a more generic solution, before accepting an answer.

You can add extra params to the ajax request that can be accessed in the success callback:
this.invoke = function(method, data, callback, error, bare) {
$.ajax({
success: onSuccess,
invokedata: {
callback: callback,
bare: bare
}
});
};
var onSuccess = function(res) {
var callback = this.invokedata.callback,
bare = this.invokedata.bare;
if (!callback) return;
var result = "";
if (res != null && res.length != 0)
var result = JSON2.parse(res);
if (bare){
callback(result);
return;
}
for (var property in result) {
callback(result[property]);
break;
}
}

+1 for excellent, excellent question - I feel your pain - this is really nicely factored as it is.
One suggestion (and maybe this is what you meant by your update)...define a wrapper for onSuccess and make it return the function you want to assign. Then call the outer function and assign it to the "success" option, passing the values it needs. Those values will be pre-assigned to the variables in the inner function. Not actually sure if this will help - you still end up with an anonymous function - but worth a try
this.invoke = function(method, data, callback, error, bare) {
$.ajax({
success: onSuccess(callback, bare);
});
};
var onSuccess = function(callback, bare) {
return function() {
if (!callback) return;
var result = "";
if (res != null && res.length != 0)
var result = JSON2.parse(res);
if (bare)
{ callback(result); return; }
for (var property in result) {
callback(result[property]);
break;
}
}
}

Related

Timing a set of functions with asyncronous subroutines

I have two functions periodically called via setInterval. The goal is to defer Function B until Function A is done (and vis versa). Currently, Function A will start, complete some of its subroutines, but not reach the end before Function B begins.
I've tried passing Function B as an argument of Function A. I am not sure if that was sufficient to create a callback. I also tried jQuery's $.when(setInterval(functionA, 10000)).then(setInterval(functionB, 5000)).
How do I ask JavaScript to wait for functions/blocks of code to finish? Thank you in advance.
Edit: Below is code very similar to my original. Sorry for not being concise.
Function A, getFruits(): There is a remote JSON that changes on its own (fruits.json). getFruits() does two things: 1) It empties an array, [allFruits] (just in case); 2) It adds all the names of fruit currently in the remote JSON to [allFruits]. Now, [allFruits] is an instanced copy of the remote JSON. Before this question, I only called getFruits() once, at startup; in other words, I did not use setInterval for getFruits().
Function B, checkFruits(): Now checkFruits() periodically (setInterval(checkFruits, 5000)) compares [allFruits] to the remote version. If any fruit was added to the remote version, checkFruits appends [allFruits] with those fruits' names; it also runs useful code (i.e. pushes the new names to an array [queue]).
For this implementation, it is important to create an initial list so only new (post-startup) fruit trigger the useful code of checkFruits(). Moreover, it is important only to add (never subtract) names from [allFruits] within a session. This is to prevent a new fruit from triggering the useful code more than once per session.
Problem: Now I want to make getFruits() (Function A) periodic. Because getFruits() empties [allFruits], it will allow the names that built up to again trigger useful code (but only once in between invocations of getFruits()). However, when I use setInterval(getFruits, 10000), there are times (in this example, always) when getFruits() overlaps with checkFruits(). When that happens, I notice only part of getFruits() finishes before checkFruits() starts. The console.log() messages appear in this order: 'getFruits() start:', 'checkFruits():', 'getFruits() end:'. Furthermore, my useful code is ran before getFruits() finishes (this is what is really undesired), and [allFruits] gets duplicates. This would not occur if getFruits() completely finished before checkFruits() jumped in.
debugging = true;
var debug = function() {
if (debugging){
console.log.apply(console, arguments)
};
}
var allFruits = [];
var queue = [];
var getFruits = function() {
allFruits = []; // Empty the list
debug('getFruits() start:', 'allFruits =', allFruits, 'queue =', queue);
$.ajax({
url: 'fruits.json',
dataType: 'json',
success: function(data) {
data.fruits.forEach(function(element) {
allFruits.push(element.name);
});
debug('getFruits() end:', 'data =', data, 'allFruits =', allFruits, 'queue =', queue);
},
});
}
var checkFruits = function() {
$.ajax({
url: 'fruits.json',
dataType: 'json',
success: function(data) {
data.fruits.forEach(function(element) {
if (allFruits.indexOf(element.name) === -1) {
queue.push(['fruit', element.name]);
allFruits.push(element.name);
}
});
debug('checkFruits():', 'data =', data, 'allFruits =', allFruits, 'queue =', queue);
}
});
}
getFruits();
setInterval(checkFruits, 5000);
// setInterval(getFruits, 10000); // When I try this, checkFruits() does not wait for getFruits() to finish.
The analogy of my actual remote resource is fruits.json. fruits.json can simply be the following:
{"fruits":[{"name":"apple","color":"red"},{"name":"banana","color":"yellow"},{"name":"tangerine","color":"orange"}]}
Again, the actual, remote JSON changes independently.
What you have here are two methods that each do asynchronouse stuff. Here are some good stack overflow posts on what that means.
Easy to understand definition of "asynchronous event"?
Does async programming mean multi-threading?
Are JavaScript functions asynchronous?
We have no idea how long it will take for an asynchronous call to finish. In your case, the AJAX request could take up to a few seconds depending on network speeds so regardless of when each of these methods are executed you CANNOT know which one will finish first. So what to do? Well, generally when you write/use an asynchronous method (like $.ajax) you give it a callback that will be executed when the asynchronous work is finished. And you have done this in the form of the success callback. And here is the good news. The success callbacks are SYNCHRONOUS (note the missing a). This means that the "useful code" in the success callback that needs to be run when a request finishes will complete (so long as none of it is async) before the "other useful code" in the other success callback is executed at all. And this works no matter which request finishes first. Each success callback will always wait for the other. So I think what was confusing you was your debug statements. If you add the following statements to your code the execution flow may make more sense:
debugging = true;
var debug = function() {
if (debugging) {
console.log.apply(console, arguments)
};
}
var allFruits = [];
var queue = [];
var getFruits = function() {
debug("getFruits: make request");
$.ajax({
url: 'fruits.json',
dataType: 'json',
success: function(data) {
debug("getFruits: start processing");
allFruits = []; // Empty the list
data.fruits.forEach(function(element) {
allFruits.push(element.name);
});
debug('getFruits: finished processing');
},
});
debug("getFruits: request sent, now we wait for a response.");
}
var checkFruits = function() {
debug("checkFruits: make request");
$.ajax({
url: 'fruits.json',
dataType: 'json',
success: function(data) {
debug("checkFruits: start processing");
data.fruits.forEach(function(element) {
if (allFruits.indexOf(element.name) === -1) {
queue.push(['fruit', element.name]);
allFruits.push(element.name);
}
});
debug("checkFruits: finished processing");
}
});
debug("checkFruits: request sent, now we wait for a response.");
}
getFruits();
setInterval(checkFruits, 5000);
// setInterval(getFruits, 10000); // When I try this, checkFruits() does not wait for getFruits() to finish.
After thinking about it I believe the only reason things may not have been behaving as expected is because you're emptying the allFruits array outside of the callback. If you move it as I have done I would think everything should work fine.
Now, I don't know why you need to re-initialize the data since each time you make the request your getting the latest information but lets roll with it. Since both methods make the same request lets consolidate that into a single method. No need to duplicate code ;). And since all of your examples have the getFruits running twice as slow as the checkFruits we could easily add a counter to accomplish the same sequence of events like so:
debugging = true;
var debug = function() {
if (debugging) {
console.log.apply(console, arguments)
};
}
var allFruits = [];
var queue = [];
var count = 0;
var doOneThing = function(data) {
//do stuff
}
var doAnotherThing= function(data) {
//do other stuff
}
var requestFruits = function() {
$.ajax({
url: 'fruits.json',
dataType: 'json',
success: function(data) {
// if count is even...or, do this every other time.
if (count % 2 === 0) {
count++;
doOneThing(data);
}
// do this everytime
doAnotherThing(data);
},
});
}
setInterval(requestFruits, 5000);
Hope this helps. Cheers.
your last code example first executes setInterval(functionA), and when the deferred execution of functionA is setup, executes setInterval(functionB), meaning that B will called +- 5 seconds after that line is executed, while functionA is called +- 10 seconds.
edit to reflect your additional information:
setInterval(function(){
functionA();
functionB();
}, 10000)
setTimeout(function(){
setInterval(functionB, 10000)
}, 5000)
This is a crude answer. I sense that callbacks can achieve this, but I am not sure how to code them, especially involving setInterval.
I create two global variables, getFruitsIsBusy = false and checkFruitsIsBusy = false. I create an IF for both getFruits() and checkFruits(). Here is getFruits():
var getFruits = function() {
if (checkFruitsIsBusy) { // New
setTimeout(getFruits, 100); // New
return; // New
} else { // New
getFruitsIsBusy = true // New
allFruits = []; // Empty the list
debug('getFruits() start:', 'allFruits =', allFruits, 'queue =', queue);
$.ajax({
url: 'fruits.json',
dataType: 'json',
success: function(data) {
data.fruits.forEach(function(element) {
allFruits.push(element.name);
});
getFruitsIsBusy = false // New; in the success function
debug('getFruits() end:', 'data =', data, 'allFruits =', allFruits, 'queue =', queue)
},
});
}
}
If also using this paradigm for checkFruits(), it seems both functions will wait for each other to finish.
Based on an analysis of the timing of two functions (A and B), consider the following solution (Chionglo, 2016):
Keep state information for each of function A and function B. The state of each function should be set within each of the respective functions.
Create a wrapper function for each of function A and function B. The wrapper function calls on the respective function, and then checks for the state of the respective function.
a. The check in wrapper function A: if function A has reached is final state, clear the interval associated with wrapper function A and schedule an interval for wrapper function B.
b. The check in wrapper function B: if function B has reached its final state, clear the interval associated with wrapper function B.
To begin the process, schedule an interval for wrapper function A.
Sample code:
var ac = Math.round(4*Math.random())+4;
var bc = Math.round(6*Math.random())+6;
var ai;
var Astate = false;
var Bstate = false;
function A() {
// Do your thing for A here.
// The following changes the “state of A” and then determines if the final state has been reached.
ac -= 1;
if (ac<1) Astate = true;
else Astate = false;
}
function B() {
// Do your thing for B here.
// The following changes the “state of B” and then determines if the final state has been reached.
bc -= 1;
if (bc<1) Bstate = true;
else Bstate = false;
}
ai = setInterval("processA()", 1000);
function processA() {
A();
if (Astate) {
clearInterval(ai);
ai = setInterval("processB()", 500);
}
}
function processB() {
B();
if (Bstate) {
clearInterval(ai);
ai = undefined;
}
}
Reference
Chionglo, J. F. (2016). An analysis for timing a set of functions. Available at http://www.aespen.ca/AEnswers/1458200332.pdf.

How to declare callback like Parse does?

Usually, I declare a function with success and fail callbacks as follow
function yoyoyo(param, successCallback, failCallback) {
// do something with param
// ...
if (success) {
successCallback('success');
} else {
failCallback('fail');
}
}
then I will use it like this
yoyoyo('abc', function(success) {
console.log(success);
}, function(err) {
console.log(err);
});
BUT, when I look into Parse Javascript Guide, they guide me to use the function like this (i.e. merge success and fail callbacks in one object?)
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.get("xWMyZ4YEGZ", {
success: function(gameScore) {
// The object was retrieved successfully.
},
error: function(object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
}
});
How can I declare my function with success and fail callbacks like parse does?
You would just change your function to accept a callbacks arg (call it whatever you want) and then access the handlers off that object:
function yoyoyo(param, callbacks) {
// do something with param
// ...
if (success) {
callbacks.success('success');
} else {
callbacks.error('fail');
}
}
then you would call it with:
yoyoyo('abc', {
success: function(status) {
},
error: function(status) {
}
});
Note though, that your code should check to ensure that the object passed in has both of the methods before attempting to call them.
That method is accepting a object that contains two function pointers. So it's a bit like creating an object thus:
var parameters = {success:function() {}, error:function(){}};
You can simply change your method thus:
function yoyoyo(param, callbacks) {
//Add some error checking to check the callbacks is in the right state
if (typeof callbacks.success != "undefined" && typeof callbacks.error!= "undefined")
{
// do something with param
// ...
if (success) {
callbacks.success('success');
} else {
callbacks.error('fail');
}
}
else {
throw "callbacks must contain a success and error method";
}
}
then call it:
.yoyoto(param, {success:function() {}, error:function(){}});
Looks like what you want is an argument that is an object with success and error functions as attributes
You can declare the function like so
function (param, callbacks) {
// do something with param
// ...
if (success) {
if(callbacks && callbacks.success) callbacks.success('success');
} else {
if(callbacks && callbacks.fail) callbacks.fail('fail');
}
}
As a side note since I see you've tagged node.js, I would also recommend you look at using a single error-first callback, which keeps your functions/apis simple and also follows a rather standard node convention.

How could I optimize this code with too many nest functions?

I'm writing a Chrome extension with the socket api(though this doc is out of date, the latest version of the api is here), and I found that the code is really hard to organize:
All the methods are under the namespace chrome.experimental.socket, I would just use socket below for simplicity.
socket.create("tcp", {}, function(socketInfo){
var socketId = socketInfo.socketId;
socket.connect(socketId, IP, PORT, function(result){
if(!result) throw "Connect Error";
socket.write(socketId, data, function(writeInfo){
if(writeInfo.bytesWritten < 0) throw "Send Data Error";
socket.read(socketId, function(readInfo){
if(readInfo.resultCode < 0) throw "Read Error";
var data = readInfo.data; // play with the data
// then send the next request
socket.write(socketId, data, function(writeInfo){
socket.read(socketId, function(readInfo){
// ............
});
});
});
})
});
})
because both socket.write and socket.read are asynchronous, I have to nest the callbacks to make sure that the next request is send after the previous request got the correct response.
it's really hard to manage these nested functions, how could I improve it?
UPDATE
I'd like to have a method send which I can use as:
send(socketId, data, function(response){
// play with response
});
// block here until the previous send get the response
send(socketId, data, function(response){
// play with response
});
How about (something like) this?
var MySocket = {
obj: null,
data: null,
start: function() { ... some code initializing obj data, ending with this.create() call },
create: function() { ... some code initializing obj data, ending with this.connect() call },
connect: function() { ... some connection code, ending with this.write() call },
write: function() { ... some writing code that updates this.data, ending with this.read() call },
read: function() { ... you probably get the idea at this point )) ... },
};
This object could be used with MySocket.start() or something. The idea is to encapsulate all data (and nested calls) within the single (yet more-o-less globally usable) object.
Or even more, one can create two objects: one purely for writing purposes, and another for purely reading, each operating with its own data, then wrap them (and their inter-calls, so to speak) into a single SocketManager object.
Consider using an asynchronous continuation passing style, where functions end with a SetInterval call with the function they were passed. Then we construct a function that entwines two functions to call each other using this mechanism. The guts of it would be like this:
var handle;
// pairs two functions
function pair(firstfunc, secondfunc, startarg) {
var callbackToFirst = function(valuetofill) {
handle = setInterval(firstfunc(valuetofill,callbackToSecond));
};
var callbackToSecond = function(valuetofill) {
handle = setInterval(secondfunc(valuetofill,callbackToFirst));
};
callbackToFirst(startarg);
}
What we are doing here is constructing a pair of mutually-calling callbacks which take a single argument, which each contain references to the two inter-calling functions. We then kick off the process by calling the first callback.
Construct the pair for an example pair of read and write functions (assuming you've set the socketId in the enclosing object definition):
// starts read/write pair, sets internal variable 'handle' to
// interval handle for control
function startReadWrite(initialarg, myDataFunc) {
var readcall = function(value, func) {
readSocket(getData(myDataFunc(func)));
};
var writecall = function(value, func) {
writeSocket(checkBytesWritten(func));
};
handle = pair(readcall, writecall, initialarg);
}
The rest of the object is like this:
function myIO() {
var socketInfo, socketId, handle;
function create(func) {
socket.create('tcp',{},function(thisSocketInfo) {
socketInfo = thisSocketInfo;
}
setInterval(func(this),0);
}
function connect(IP, PORT, func) {
socket.connect(p_socketId, IP, PORT, function() {
if(!result) throw "Connect Error";
setInterval(func(result),0);
});
}
function readSocket(func) {
socket.read(p_socketId, function(readInfo){
setInterval(func(readInfo),0);
});
}
function writeSocket(data, func) {
socket.write(p_socketId, data, function(writeInfo){
setInterval(func(writeInfo),0)
});
}
function checkBytesWritten(writeInfo, func) {
if(writeInfo.bytesWritten < 0) throw "Send Data Error";
setInterval(func(writeInfo),0);
}
function getData(readInfo, func) {
if(readInfo.resultCode < 0) throw "Read Error";
var data = readInfo.data;
setInterval(func(data),0);
}
//** pair and startReadWrite go here **//
}
Finally the call to set the whole thing going:
var myIOobj = new myIO();
myIOobj.create(startReadWrite(myDataFunc));
Notes:
This is meant to demonstrate a style, not be ready code! Don't just copy and paste it.
No, I haven't tested this; I do javascript but not Chrome API stuff yet. I'm focussing on the callback mechanisms etc.
Be careful with the different classes of callback; single argument callbacks (like the read and write callbacks) which take a single value (as presumably defined by the API), and 2 argument callbacks (like most of the methods) which take an argument and a function to call at the end.
The getData method takes a callback and passes data to it; this callback (myDataFunc) is the function that actually gets to use the data. It needs to take a callback as a second argument and call it synchronously or asynchronously.
TLDR: Consider using asynchronous calls to avoid the nesting. I've given a vague example of a mechanism to have two functions call each other continuously using this style as seems to be needed.
Although I call it asynchonous, the setInterval calls will execute serially, but the key is that the stack is cleared after the parent call is done, rather than adding endless layers with nesting.

Any way to do a synchronous PageMethods call?

I'm trying to do this:
function DelBatch()
{var userInfo = get_cookie("UserInfo");
PageMethods.DeleteBatchJSWM(userInfo, function(result)
{window.location = "BatchOperations.aspx";});
}
But it still runs asynchronously. I need the browser to actually wait until my code-behind is finished executing, then it can be refreshed
There's a listbox loaded with values that were just deleted from the database, they shouldn't be visible. Problem I have is the window location refreshes before the code-behind is executed, and nothing seems like it was deleted to the user.
Call it using jQuery ajax instead? It features an option (async) where you can select sync/async mode: http://api.jquery.com/jQuery.ajax/
This excellent article tells you how best to call PageMethods from jQuery: http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/
Essentially, all you will need to do is this:
$.ajax({
type: "POST",
async: false,
url: "yourpage.aspx/DeleteBatchJSWM",
data: "{ put json representation of userInfo here }",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
window.location = "BatchOperations.aspx";
}
});
Look at Crockford's JSON stringify for a json formatting solution.
If you want to avoid using jQuery, a work around would be to use another PageMethod in which you check the status of the operation using the javascript setInterval function. It is a little messy, but it does the job if you want zero jQuery and it mimics the synchronicity you seek. I use it for large operations in which I want to update a progress bar to the client or something. Here would be an example of how you would do this given what code you posted:
function DelBatch()
{
var userInfo = get_cookie("UserInfo");
PageMethods.DeleteBatchJSWM(userInfo, function(result) {window.location = "BatchOperations.aspx";});
var status;
//Check to see if it has completed every second
var myInterval = setInterval(function ()
{
PageMethods.CheckDeleteBatchStatus(OnSuccess);
if (status == "Finished")
{
clearInterval(myInterval);
//Finished Deleting. Call your window refresh here
WindowRefresh();
}
}, 1000);
function OnSuccess(result)
{
status = result;
}
}
Code Behind:
[WebMethod]
public static string CheckDeleteBatchStatus()
{
string status = GetDeleteBatchStatus(); //some function to get the status of your operation
return status;
}
I came across this site:
http://abhijit-j-shetty.blogspot.com/2011/04/aspnet-ajax-calling-pagemethods.html
that had a great method for handling Synchronous PageMethod calls.
The javascript code is as follows:
// Make sure page methods operate synchronously
XMLHttpRequest.prototype.original_open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
async = false;
var eventArgs = Array.prototype.slice.call(arguments);
var q = 0;
return this.original_open.apply(this, eventArgs);
}
// Make a generic WebMethod caller:
function WebMethodCall(FunctionName, callingobj) {
var OnSuccess = function (result, userContext, methodName) {
callingobj.push(result);
}
var OnFailure = function (error, userContext, methodName) {
callingobj.push(error.get_message());
}
PageMethods[FunctionName](OnSuccess, OnFailure);
}
// OK, this is kludgy, but here goes. In order to have a synchronous PageMethod call
// we need an object that persists in the namespace to stuff the result value into (like an array)
// Essentially I'm emulating a ByRef call.
// ThisResult is an empty list. The WebMethodCall function sticks a value into that list.
// The code that makes the PageMethods get called synchronously is in Common.js
// Use the functions
var ThisResult = []; // This must be of a type which persists in the namespace
WebMethodCall('HelloWorld', ThisResult);
return ThisResult[0];
Using jQuery was first recommended back in 2009.
Another (extremely verbose) option is implementing a synchronous WebRequestExecutor as shown here (2007-07-04), and perfected here (2007-10-30). The gist of the technique is to copy the ASP.NET AJAX Sys.Net.XMLHttpExecutor as a new class named Sys.Net.XMLHttpSyncExecutor and change the call to xmlHttpRequest.open to pass false as the last parameter to force synchronous operation.
The synchronous executor can be plugged into all requests using WebRequestManager like this:
Sys.Net.WebRequestManager.set_defaultExecutorType('Sys.Net.XMLHttpSyncExecutor');
or you may want to switch it up per-request just before it is invoked:
Sys.Net.WebRequestManager.add_invokingRequest(function(sender, args) {
if (iFeelLikeRunningThisRequestSynchronously) {
args.get_webRequest().set_executor(new Sys.Net.XMLHttpSyncExecutor());
}});
This discussion is the source for most of these links and a few more.
I wrote this, that lets you call a PageMethod synchronously. It also will just return the result of the method, and throw an error that can be caught in a try/catch block, so you don't need to worry about supplying onSuccess and onError functions.
function synchronusPageMethod(method) {
XMLHttpRequest.prototype.original_open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
async = false;
var eventArgs = Array.prototype.slice.call(arguments);
return this.original_open.apply(this, eventArgs);
};
var result;
var error;
var args = Array.prototype.slice.call(arguments).slice(1);
args.push(function (res) {
result = res;
});
args.push(function (err) {
error = err;
});
method.apply(null, args);
XMLHttpRequest.prototype.open = XMLHttpRequest.prototype.original_open;
if (error !== undefined) {
throw error;
} else {
return result;
}
}
Use it like this:
try {
var result = synchronusPageMethod(PageMethods.myMethod, argument0, argument1);
console.log(result);
} catch(error) {
console.log(error);
}

Good way of avoiding a second ajax call

So I'm doing a an ajax call in this function somewhat like this:
function getCount() {
$.get("/People/getCount", function (data) {
if (data && data != "") {
// lots of code in here
}
What I'm doing in another function is making a second call like this:
function worldPeople() {
return $.get("/People/getCount", function (data) {
if (data != 0) {
var target = $("#worldNumbers").find("span");
target.html(data.length).digits();
}
})
}
So I really would like to avoid making that second call. Is there any good way in avoiding that? Maybe do some chaining or such, reusing the callback from the first one? I've heard that its bad practice to do several calls.
Regards
Would like to thank all who answered. In the end did not use any of the solutions, I solved it in another way. I'm sure most of the examples you gave me were really good. Do not know how to do with accepting answers. Accept all or none?! Thanks!
You could create a simple data store:
App.store = function () {
this.people = null;
this.count
loadPeople = function () {
if(this.people === null) {
$.get("/People/getCount", function (data) {
if (data != 0) {
this.count = (data.length).digits();
this.people = data;
}
}
};
}
What about store count of peoples in hidden field? And than check this field before sending request.
You can achieve this by handling your Ajax requests using some sort of cache. I use a cache that saves the information retrieved based on the url it called. If another function sets off the same request the cache returns the alraedy fetched data.
What you do need to do as well though is check if the data is outdated so you can refetch it if necessary.
Well, you can just send the function pointer to the function that executes $.get
basically you would then do this:
function worldPeople() {
getCountFromServer(function(data){
//do sth with data
});
}
function getCount() {
getCountFromServer(function(data){
//do sth with data
});
}
function getCountFromServer(callback) {
return $.get("/People/getCount", function (data) {
if (data)
callback(data);
});
}
I generally use a caching module pattern for this kind of thing:
// create a quick singleton to store cached data
var People = (function() {
// private variable to act as cache
var count;
// function to get cached data
// note: You have to assume it's always asynchronous
function getCount(callback) {
// have we loaded the data yet?
if (count===undefined) {
// cache miss: load the data, store it, do the callback
$.get("/People/getCount", function (data) {
count = data;
callback(data);
}
} else {
// cache hit - no need to reload
callback(count);
}
}
// provide access to the getter function
return {
getCount: getCount
};
}());
The first time you hit the cache, it'll load from the server; the second time it will load from the private variable.
// will load the data asynchronously
People.getCount(function(count) {
alert("First hit: " + count);
});
// will use the cached data
People.getCount(function(count) {
alert("Second hit: " + count);
});
Depending on the complexity you want to support, you could add additional features like expiring the cache after a particular interval, caching multiple calls (potentially keyed to the AJAX URL), etc. I like to keep the API simple and not reference the AJAX URLs - that way your cache acts like an abstracted service layer, and you can create other cache implementation to work with different data sources - useful for things like stubbing out data before you've implemented your server-side AJAX handlers.

Categories