Making functions wait until AJAX call is complete with jQuery - javascript

Im trying to develop a class in JavaScript I can use to access a load of data that is gathered by an AJAX request easily. The only problem is I need to make the members of the class accessible only once the AJAX call is complete. Ideally what I would like to end up is something where by I can call this in a script:
courses.getCourse('xyz').complete = function () {
// do something with the code
}
And this will only fire after the AJAX call has been complete and the data structures in the "class" are ready to be used. Ideally I dont want to have to create a .complete member for every function in the class
Here is the "class" I am trying to make so far:
var model_courses = (function() {
var cls = function () {
var _storage = {}; // Used for storing course related info
_storage.courses = {}; // Used for accessing courses directly
_storage.references = new Array(); // Stores all available course IDs
var _ready = 0;
$.ajax({
type: "GET",
url: "data/courses.xml",
dataType: "xml",
success: function(xml) {
$(xml).find("course").each(function() {
_storage.courses[$(this).attr('id')] = {
title : $(this).find('title').text(),
description : $(this).find('description').text(),
points : $(this).find('points').text()
}
_storage.references.push($(this).attr('id'))
})
}
})
console.log(_storage.courses)
}
cls.prototype = {
getCourse: function (courseID) {
console.log(cls._storage)
},
getCourses: function () {
return _storage.courses
},
getReferences: function (),
return _storage.references
}
}
return cls
})()
At the moment getCourse will be fired before the AJAX request is complete and obviously it will have no data to access.
Any ideas will be greatly appreciated, im stuck on this one!

jQuery already handles this for you using deferred objects, unless i'm misunderstanding what you are looking for.
var courses = {
getCourse: function (id) {
return $.ajax({url:"getCourse.php",data:{id:id});
}
};
courses.getCourse("history").done(function(data){
console.log(data);
});
I know this isn't exactly what you are looking for, I'm hoping it's enough to push you in the right direction. Deferred objects are awesome.

The following changes allow you to make the AJAX request just once and you can call your function like
courses.getCourse('xyz', function(course){
// Use course here
});
Here are the changes
var model_courses = (function() {
// This is what gets returned by the $.ajax call
var xhr;
var _storage = {}; // Used for storing course related info
_storage.courses = {}; // Used for accessing courses directly
_storage.references = []; // Stores all available course IDs
var cls = function () {
xhr = $.ajax({
type: "GET",
url: "data/courses.xml",
dataType: "xml",
success: function(xml) {
$(xml).find("course").each(function() {
_storage.courses[$(this).attr('id')] = {
title : $(this).find('title').text(),
description : $(this).find('description').text(),
points : $(this).find('points').text()
}
_storage.references.push($(this).attr('id'))
});
}
});
}
cls.prototype = {
// Made changes here, you'd have to make the same
// changes to getCourses and getReferences
getCourse: function (courseID, callback) {
if (xhr.readyState == 4) {
callback(_storage.courses[courseID]);
}
else {
xhr.done(function(){
callback(_storage.courses[courseID]);
})
}
},
getCourses: function () {
return _storage.courses
},
getReferences: function (),
return _storage.references
}
}
return cls
})()
As a side note, your module pattern will not work very well if you need to instantiate two of these model_courses objects, since the storage objects are all shared in your self calling function's closure. You usually don't mix the module pattern with prototypes (returning a constructor from a module), unless you really know what you are doing, that is, the shared closure variables work as static properties of your class.
This is what I would do if I were you (since you really want private variables)
function ModelCourses() {
var storage = {
courses: {},
references: []
};
var xhr = $.ajax({
type: "GET",
url: "data/courses.xml",
dataType: "xml",
success: function(xml) {
$(xml).find("course").each(function() {
storage.courses[$(this).attr('id')] = {
title : $(this).find('title').text(),
description : $(this).find('description').text(),
points : $(this).find('points').text()
}
storage.references.push($(this).attr('id'))
})
}
});
this.getCourse = function(courseId, callback) {
function getCourse() {
callback(storage.courses[courseID])
}
if (xhr.readyState == 4) {
getCourse();
}
else {
xhr.done(getCourse);
}
};
}

in getStorage either add a check to see if there is any data to pilfer (preferred), or make the "actual" method private than publicize it when it has items it can access. (I would recommend the first though otherwise you'll get exceptions about calling a method that doesn't exists on an object).

You can define a function getData that would perform the ajax request and that would take the getCourse as a callback.
The getData could possibly store locally the result of the Ajax call and test the local storage before performing the ajax call.
You could also specify a private member to allow the ajax call to be run only once.
You might want to check underscore.js for some handy tool
Here is a short example code :
cls.prototype.getData = function(callback) {
/*perform ajax call or retrieve data from cache*/
callback()
}
cls.prototype.getCourse = function(id) {
this.getData(function() {
/*do something with the data and the id you passed*/
}
}

Related

AJAX wrapper for jQuery

I want to make a convenience method for my Ajax calls as it is used extensively in the project.
As of now a typical call in the project looks like this.
$.post(
"url",
{
param1: value1,
param2: value2
},
function (data) {}
);
This call is repeated multiple times in the project with different parameters so I would like to be able to create a function to which I can pass the parameters and it will handle the entire Ajax call without me having to write the code every time.
Expected Output:
var data= {firstName:"John", lastName:"Doe", age:46};
do_ajax_request(data);
The function do_ajax_request in turn contains the actual Ajax code which makes the actual request and handles the result.
If possible I would also like for it to return a callback in case I need to perform any extra operations, would a promise work for that?
This would be a global function so I can access it from any JavaScript file.
So many complicated answers for something jQuery supports out of the box. Turning my comment to an answer.
You are basically just coding a wrapper for a wrapper so you do no have to recode some basic lines. No harm in that since it is easy to make the change in one place vs many.
So defined your function and just return the Ajax object that jQuery has. You can than use the done, fail, always methods.
function do_ajax_request (data) {
return $.post("url", data);
}
do_ajax_request({"foo":"bar"})
.done( function(){})
.fail(function(){})
do_ajax_request({"foo":"bar"})
.done( function(){})
.fail(function(){})
If you want to have common code inside, you can do that too, basic idea for an error handler...
function do_ajax_request (data) {
var xhr = $.post("url", data);
xhr.fail(function () {
console.log(arguments)
});
return xhr;
}
I have written several jQuery plug-ins for use in my projects, and have brought along my ajax call method in nearly everyone. Here is a snippet of it from one of my projects. Enjoy!
Method Signature:
obj = An object you want to pass to the ajax call in the data parameter. Pass null if not needed.
method = ajax methods: POST, GET, PUT, DELETE, etc. Default is GET.
endPoint = Url to call.
returnType = html, json, text, etc.
success = callback method when the call is successful.
beforesend = method to call before the send. This is useful when you need to set headers before a call.
failure = callback method when the call is unsuccessul.
var _api = {
call: function (obj, method, endPoint, returnType, success, beforesend, failure) {
obj = obj === null || undefined ? {} : obj;
$.ajax({
method: method || 'GET',
data: !$.isEmptyObject(obj) ? JSON.stringify(obj) : null,
contentType: function () {
switch (returnType) {
case 'json':
return 'application/json';
case 'text':
return 'text/plain';
case 'buffer':
return 'arraybuffer';
case 'html':
default:
return 'text/html';
}
}(returnType === 'json' ? 'application/json; charset=utf-8' : ''),
url: endPoint,
dataType: returnType,
beforeSend: function (xhr, obj) {
if (beforesend) {
beforesend(xhr, obj);
} else {
_api.showLoader();
}
}
}).done(function (data, textStatus, xhr) {
if (success) success(data)
}).fail(function (data, textStatus, xhr) {
if (failure) failure()
}).always(function () {
// Implement code here that you want to run whenever the call is complete regardless of success or failure.
});
}
}
You could create a prototype to with a constructor to handle the input - make the request and handle the response:
ajax.requests = function ( data ) {
this.data = data;
return this.doRequest();
};
ajax.requests.prototype = {
doRequest : function () {
var _this = this;
$.ajax({
data: _this.data
}).done(function(data) {
Handle response and return!
});
}
};
// USAGE
var response = new ajax.requests( yourData );
By returning the $.post, you can use a callback like .done(), chain them together with .then(), etc.
function do_ajax_request(data) {
return $.post( ... ); //RETURN the object
}
var myData = { ... };
do_ajax_request(myData).done(function(result) {
console.log("AJAX complete: " + result);
});
Just another take on this that maybe you hadn't considered. Rather than trying to wrap what is essentially already a wrapper, consider encapsulating your common functionality, like handling errors and dealing with results and using this when executing an ajax request with the existing jQuery ajax wrapper(s)
function handleError(e){
// your common error handling
}
function handleResult(result){
// your common result handling
}
// then every time you execute a request, use your shared functionality
$.post(url, data)
.fail(handleError)
.done(handleResult);
Using code below, you'd need to import config object or declare on top of the functions.
I made two versions for POST and GET respectively
function getJSON(param, absoluteRestUrl = '') {
if (!absoluteRestUrl) {
absoluteRestUrl = config.adminRestEndpoint; // defaultUrl
}
return new Promise(async (resolve, reject) => {
let res = null;
res = await $.getJSON(absoluteRestUrl, param);
resolve(JSON.parse(JSON.stringify(res)));
});
}
function postJSON(param, absoluteRestUrl = '') {
if (!absoluteRestUrl) {
absoluteRestUrl = config.adminRestEndpoint; // defaultUrl
}
return new Promise(async (resolve, reject) => {
let res = null;
res = await $.post(absoluteRestUrl, param, null, 'json');
resolve(JSON.parse(JSON.stringify(res)));
});
}

Passing the 'response' or 'data' AJAX return into a namespace function

I am trying to clean up my code by namespacing which is new to me. I have a very basic app that makes an Ajax request and then does a load of stuff with the response. The code is starting to look messy so I want to namespace it and then call the namespace function that takes the AJAX response as the argument.
1. Can and should this be done?
2. If so how, here is the code
var GETDATA = {
myAlert: "this variable is the property of a namespace",
// response?: ???? do i need to declare response var here somehow
myNSFunction: function () {
alert(this.myAlert)
}
//theFunctionIWant: function (response??) {
// takes the response from ajax request
// does some stuff to it.
//}
};
$(document).ready(function() {
$( "#my-form" ).submit(function( event ) {
$.ajax({
type: 'GET',
url: "http://localhost:3000/DATA"
})
.done(function(response) {
//GETDATA.theFunctionIWant();
});
});
Any help much appreciated.
var GETDATA = {
myAlert: "this variable is the property of a namespace",
myNSFunction: function () {
alert(this.myAlert)
},
theFunctionIWant: function (response) {
console.log(response);
}
};
$(document).ready(function () {
$("#my-form").submit(function (event) {
$.ajax({
type: 'GET',
url: "http://localhost:3000/DATA"
})
.done(function (response) {
GETDATA.theFunctionIWant(response);
}
});
});
Should you do this?
Difficult to pass a judgment without understanding your overall goal and the scale of things, but mostly, no. You have to ask yourself, what benefit does this really offer? I mean, the handler could even be a function itself, not necessarily namespaced. What is the justification for namespacing it?
Will you have a group of response processing functions that are similar? Are you trying to build a store of received responses and related functions that act on the responses? Are you worried about conflicts resolution of some sort that requires a namespace to resolve with? etc.
A better approach: Consider closures for a private scope.
var RequestProcessors = (function() {
var thisIsPrivate = "Ok",
thisIsPrivateToo = "Fine";
return {
processorOne: function(resp, arg) { /* logic here */ },
processorTwo: function(resp, arg) { /* logic here */ }
}
})();

jQuery updating class variable values not working

I am writing a class in JavaScript for the first time and I am having some trouble writing new data to a class variable. I've been trying all sorts for hours but nothing seems to work!
function ClassName(productId) {
//create variables
this.productId = productId;
this.shop = [];
this.product = [];
//method that calls for response. On success will return {"status" : "success", "shop" : "someshop.com"}
this.auth = function() {
$.ajax({
url: "http://website.com/api/auth/",
dataType: "jsonp",
success: function(data) {
authCallback(data); //use callback to handle response
},
error: function() {
console.log("bad auth");
}
});
}
var authCallback = function(r) {
//using console.log(r) output the response OK
this.shop = r; //this runs with no errors
}
}
Now, as yo can see in the authCallback method I'm setting this.shop = r; but then if i refer back to this variable its still at its default value of [] .
var class = new ClassName(1);
class.auth();
console.log(class.shop); //this outputs []
I've also tried this in the Javascript console writing each line after each stage had been completed(waited for a response from class.auth() and output from authCallback() before then calling console.log(class.shop);
So, what am I doing wrong? Why isn't the variable updating to its new value?
When you just write:
authCallback(data);
then within authCallback you will have the wrong value of this, it'll either be null or the global object (depending on whether you're in strict mode or not).
Use:
success: authCallback.bind(this)
to ensure that this inside the callback actually represents your object.
You should also note that you cannot access this.shop until after the callback has completed. A more idiomatic implementation using modern jQuery techniques would be this:
this.auth = function() {
return $.ajax({
url: "http://website.com/api/auth/",
dataType: "jsonp"
}).done(this.authCallback.bind(this)).fail(function() {
console.log("bad auth");
});
};
this.authCallback = function(r) {
this.shop = r;
return this;
}
followed by:
var clazz = new ClassName(1);
clazz.auth().then(function(c) {
console.log(c.shop);
});

JavaScript module that uses AJAX to set and return variables

I'm trying to write a module that makes two AJAX calls and sets variables to the results of those calls. I'd like to be able to access the results of those calls like myModule.firstCall and get the result of the AJAX call and not the promise.
var ajaxModule = (function () {
var subFolderData = {},
rootData = {};
var subFolderFile = function () {
return $.ajax({
url: 'Content/testData.json',
dataType: 'json'
});
}
var rootFile = function () {
return $.ajax({
url: 'testDataRoot.json',
dataType: 'json'
});
}
//only returning promise here, how to return $.when the call is done?
return {
rootFile: rootFile().done(function (data) {
subFolderData = data;
}),
subFolderFile: subFolderFile().done(function (data) {
rootData = data;
})
}
})();
//this prints out the dat as expected, but can I store the results in a variable
//to be accessed like ajaxModule.rootFile?
console.log(ajaxModule.rootFile.done(function (data) {
console.log(data);
}));
No, you cannot return the result from an asynchronous call.
Assigning them to a global (or higher-scope) variable, such as subFolderData or rootData in your example, is possible, but does not make sense because you do not know when the value will be available.
Storing the promises for the values, like your ajaxModule.subFolderFile and ajaxModule. rootFile, and always incorporating them when needing to access the data, is the way to go.

Specifying a callback in jQuery.ajax to OpenCalais SemanticProxy service

I'm trying to query the OpenCalais service semanticproxy.com. Unfortunately, their url format is as follows:
http://service.semanticproxy.com/processurl/APIKEY/jsonp:handler_function/http://en.wikipedia.org/wiki/Germany
notice that the function callback, is not in a callback=? parameter, but rather follows the response format (jsonp:). This means that I can't use .getJSON, but rather need to use the .ajax method. So I have the following object definition:
function Subject() {
}
Subject.prototype.populate = function(page_title) {
var url = "http://service.semanticproxy.com/processurl/APIKEY/jsonp:handler/http://en.wikipedia.org/wiki/" + page_title;
$.ajax({url: url, dataType: "script", type: "GET", cache: false, callback: null, data: null});
};
var handler = function (data) {
// do stuff with the returned JSON
};
s = new Subject();
s.populate("Germany");
This works fine. But what I really want to do is set properties of my Subject object. But I don't know how to create a function in the context of the Subject that will be able to be used as the callback. i.e:
Subject.prototype.handler = function(data) { this.title = data.title }
Any ideas?
You'd have to set a function on the window object. This is essentially (I think) what jQuery does with its .getJSON method. The below is a bit hacky but hopefully it points you in the right direction:
function Subject() {
}
Subject.prototype.populate = function(page_title) {
// Save context object
var subject = this;
// Create function name like subjectHandler1281092055198
var functionName = "subjectHandler" + new Date().getTime();
window[functionName] = function(data) {
// Invoke function with saved context and parameter
subject.handler.call(subject, data);
}
var url = "http://service.semanticproxy.com/processurl/APIKEY/jsonp:" + functionName + "/http://en.wikipedia.org/wiki/" + page_title;
$.ajax({url: url, dataType: "script", type: "GET", cache: false, callback: null, data: null});
};
Subject.prototype.handler = function (data) {
// do stuff with the returned JSON
};
s = new Subject();
s.populate("Germany");
I don't think you're going to be able to do this, just because of how JSONP works, look at how it actually comes back to the browser, it pretty much does this:
<script type="text/javascript">
handler({ title: "Germany", ...other properties... });
</script>
There's no way to maintain a reference here, you could do one request at a time, or keep an object map for each subject, but there's no way to do it in the JSONP request.
An object map would look something like this:
//delcare this once for the page
var subjects = {};
//do this per subject
var s = new Subject();
s.populate("Germany");
subjects["Germany"] = s;
Then in your hanldler, if any of the data properties is "Germany", you could get it that way, for example:
var handler = function (data) {
var subject = subjects[data.title];
//subject is your Germany subject, use it, go nuts!
};

Categories

Resources