What does an asynchronous AJAX call return? - ajax

I am trying to create a test case to monitor progress of multiple parallel asynchronous server tasks. I have the code sort-of working, but there are several pieces I don't understand. First, what does the $.ajax call below return? In theory, it should return undefined, but that doesn't seem to be the case.
function doParallel() {
var promiseA, promiseB, handleSuccess, handleFailure;
var dataA = JSON.stringify({ size: a });
var dataB = JSON.stringify({ size: b });
promiseA = $.ajax({
url: testGlobal.urlA,
data: dataA,
type: "POST",
async: true,
contentType: 'application/json; charset=utf-8',
dataType: "json",
success: function (rtnData) {
// Get the result
result = (rtnData === undefined) ? null : $.parseJSON(rtnData.d);
},
error: function (xhr, textStatus, errorThrown) {
// Whoops! didn't work
reportAjaxError(xhr, textStatus, url, data);
},
complete: function (xhr, textStatus) {
// Errors have already been handled, so only
// deal with success cases
}
}); <--- WHAT GETS RETURNED TO PROMISE HERE?
... (same code for promiseB, etc.
var notifyingPromiseA = intervalPromise(2000, 'a');
var notifyingPromiseB = intervalPromise(2000, 'b');
...
promiseA.done(function() {
log("A done");
}
promiseB.done(function() {
log("B done");
}
$.when(promiseA, promiseB).done(function() { log ("All done") });
}
function intervalPromise(millis, source) {
var deferred = $.Deferred();
//checkProgress();
log("Checking progress on " + source);
var id = setInterval(function () {
deferred.notify();
if (testGlobal.aDone && testGlobal.bDone) {
clearInterval(id);
deferred.resolve();
}
}, millis);
return deferred.promise();
}
...

$.ajax() returns the XMLHttpRequest object. As of jQuery v1.5, $.ajax() also implements and returns a Promise / Deferred interface.
http://api.jquery.com/jQuery.ajax/
http://api.jquery.com/category/deferred-object/
With a Promise, you can chain additional callbacks based on the results of the original ajax call.
// setup interval / timer to update UI not finished / still working logic
$.ajax().done(function() {
// clear UI not fninshed / still working logic
});

mmm.. official docs say:
The $.ajax() function returns the XMLHttpRequest object that it
creates. Normally jQuery handles the creation of this object
internally, but a custom function for manufacturing one can be
specified using the xhr option. The returned object can generally be
discarded, but does provide a lower-level interface for observing and
manipulating the request. In particular, calling .abort() on the
object will halt the request before it completes.
http://api.jquery.com/jQuery.ajax/

As of jQuery 1.5, jQuery.ajax() (and various ajax shortcut methods) returns a jqXHR object, which is a superset of the browser's native XMLHttpRequest object and implements inter alia the Promise interface.
Read more about the jqXHR object here.

Related

Ajax call always triggers fail handler even though success is returned by the server

The following JavaScript always triggers the fail handler even though the return value is success from the server side:
$.ajax(payload)
.done(function(data, statusText, jqxhr) {
document.getElementById('myModal').innerHTML = "<p>Record Saved ... </p>";
modal.style.display = "block";
refresh_html_page(document.getElementById("sheetname").value);
})
.fail(function(jqxhr, statusText, errorThrown) {
document.getElementById('myModal').innerHTML = "<p>Record Not Saved ... </p>";
modal.style.display = "block";
refresh_html_page(document.getElementById("sheetname").value);
})
.always(function () {
// Re-enable the inputs
$inputs.prop("disabled", false);
});
Returned JSON string:
[{"result":"success","row":11}]
Any thoughts?
Good news. I was able to crack it. The solution was as follows:
Set up a call back function in the payload
Have a dummy action in the newly created call back function
Prefixed the call back function name in the server side while creating the jasonp response
Client side:
function handleJSONPResponse(data, status, request) {
console.log('response', data);
}
// Fire off the request to /form.php
var payload = {
crossDomain: true,
url: "https://script.google.com/macros/s/XXXXXXXXXXXXXXXXX/exec",
method: "POST",
dataType: "jsonp",
data: serializedData,
jsonpCallback: 'handleJSONPResponse'
};
Server Side (e is the payload sent from client):
return ContentService
.createTextOutput(e.parameters.callback + '(' + JSON.stringify({"result":"success", "row": nextRow})+ ')')
.setMimeType(ContentService.MimeType.JAVASCRIPT);
It was wonderful solving the problem. Thank you very much for your kind inputs and encouragement. Much appreciated.

Bypass Ajax request within javascript promise in Unit Testing

I have a function called getStudentData(),returns resolved data.
Inside getStudentData(), I have an Ajax request.
I want to Bypass Ajax request in my unit test case using Mocha , so that when i make a call to getStudentData(), the data should be returned.
Please find the code below:
getStudentData: function() {
return studentData || (studentData = new Promise(function(resolve, reject) {
var request = {
//request data goes here
};
var url = "/student";
$.ajax({
url: url,
type: "POST",
data: JSON.stringify(request),
dataType: "json",
contentType: "application/json",
success: function(response, status, transport) {
//success data goes here
},
error: function(status, textStatus, errorThrown) {
reject(status);
}
});
}).then(function(data) {
return data;
})['catch'](function(error) {
throw error;
}));
}
Please let me know how to Bypass Ajax request By stubbing data using sinon.js .so that when i make a call to getStudentData() , data should be returned.
First of all doing:
then(function(data){ return data; })
Is a no-op. So is:
catch(function(err){ throw err; });
Now, your code uses the explicit construction anti-pattern which is also a shame, it can be minimized to:
getStudentData: function() {
var request = {
//request data goes here
};
var url = "/student";
return studentData ||
(studentData = Promise.resolve($.ajax({
url: url,
type: "POST",
data: JSON.stringify(request),
dataType: "json",
contentType: "application/json" })));
}
Now, that we're over that, let's talk about how you'd stub it. I'd do:
myObject.getStudentData = function() {
return Promise.resolve({}); // resolve with whatever data you want to test
};
Which would let you write tests that look like:
it("does something with data", function() { // note - no `done`
// note the `return` for promises:
return myObj.getStudentData().then(function(data){
// data available here, no ajax request made
});
});
Although in practice you'll test other objects that call that method and not the method itself.

Wait for Ajax call to finish

I have a situation where in I m doing a number of AJAX calls using jquery and in turn returning JSON data from those calls into some variables on my page.
The issue is that the Ajax call takes a little time to get processed and in the mean time my control shifts to next statement where I intend to use the output of AJAX call.
Since the call takes time to return the data I am left with empty object that fails my function.
is there any way where I can wait for the finish of AJAX call to happen and proceed only when the result is returned from the call???
so this is my code where in I am trying to return transactionsAtError to some other jquery file where the control shifts to next statement before this call gets executed
this.GetTransactionAtErrors = function (callback) {
var transactionsAtError;
$.ajax({
url: ('/Management/GetTransactionsAtError'),
type: 'POST',
cache: false,
success: function (result) {
if (result && callback) {
transactionsAtError = (typeof (result) == "object") ? result : $.parseJSON(result);
}
}
});
return transactionsAtError;
}
Assuming you are using jQuery's $.getJSON() function, you can provide a callback function which will be executed once the data is returned from the server.
example:
$.getJSON("http://example.com/get_json/url", function(data){
console.log("the json data is:",data);
});
EDIT:
After seeing the code you added i can see what's your problem.
Your return transactionsAtError; line runs independently of the ajax call, i.e it will run before the ajax is complete.
you should just call your callback inside your success: function.
example:
this.GetTransactionAtErrors = function (callback) {
$.ajax({
url: ('/Management/GetTransactionsAtError'),
type: 'POST',
cache: false,
success: function (result) {
if (result && callback) {
var transactionsAtError = (typeof (result) == "object") ? result : $.parseJSON(result);
callback(transactionsAtError);
}
}
});
}
When you have your result in scope you can check wait for ongoin ajax calls to finish by using es6 promise:
function ajaxwait()
{
return(new Promise(function(resolve, reject) {
var i = setInterval(function() {
if(jQuery.active == 0) {
resolve();
clearInterval(i);
}
}, 100);
}));
}
You can use this like.
ajaxwait().then(function(){ /* Code gets executed if there are no more ajax calls in progress */ });
Use an es6 shim like this https://github.com/jakearchibald/es6-promise to make it work in older browsers.

Retain the context of JQuery ajax function

I have an old code which is dependant on JQuery 1.3.2 that uses the following ajax call
function ChangeContent(url, somepageobject) {
var xhrobj = $.ajax({
url: url,
context: somepageobject,
callback: doFurtherStuff,
success: function(data) {
somepageobject.html($(data));
this.callback.call(this.context[0], data, "ok"); // >> Code breaks here
}
});
return xhrobj;
}
Problem with the code above is that this.callback is null when I upgraded to JQuery 1.8.1, most importantly the ChangeContent function is being used in different places and is outside my control (its used as as an API for external users...etc). An example of the usage of the above is like this:
xhr_object = ChangeContent("/someurl, $("#resultContainer"));
function doFurtherStuff(responseText, statusText, XMLHttpRequest)
{
var identifier = '#' + this.id;
...
}
Notice that the doFurtherStuff must have the correct "this" object value which is the context specified in ChangeContent function. When I tried to use different deferred then() ...etc. functions in JQuery 1.8.1 to solve the above this.callback.call(this.context[0], data); problem after the upgrade the "this" object in the callback function had different value since I guess the new JQuery library handles that differently.
Is there anyway to fix the error above while limiting the change to ChangeContent function only as I try to avoid asking all users to change the way they call and handle call backs from that function?
When you add the context option, you are telling jQuery what this should be inside of the success callbacks. That means you can't access the options passed into the ajax request. Either don't supply a context, or pass in the callback manually.
function ChangeContent(url, somepageobject) {
var callback = doFurtherStuff;
var xhrobj = $.ajax({
url: url,
context: somepageobject,
success: function(data) {
somepageobject.html($(data));
callback.call(this[0], data, "ok"); // >> Code breaks here
}
});
return xhrobj;
}
Update:
If you want to instead continue using your code as-is, simply rename the context property.
function ChangeContent(url, somepageobject) {
var xhrobj = $.ajax({
url: url,
thecontext: somepageobject,
callback: doFurtherStuff,
success: function(data) {
somepageobject.html($(data));
this.callback.call(this.thecontext[0], data, "ok"); // >> Code breaks here
}
});
return xhrobj;
}

jQuery.ajax() sequential calls

Hey. I need some help with jQuery Ajax calls. In javascript I have to generste ajax calls to the controller, which retrieves a value from the model. I am then checking the value that is returned and making further ajax calls if necessary, say if the value reaches a particular threshold I can stop the ajax calls.
This requires ajax calls that need to be processes one after the other. I tried using async:false, but it freezes up the browser and any jQuery changes i make at the frontend are not reflected. Is there any way around this??
Thanks in advance.
You should make the next ajax call after the first one has finished like this for example:
function getResult(value) {
$.ajax({
url: 'server/url',
data: { value: value },
success: function(data) {
getResult(data.newValue);
}
});
}
I used array of steps and callback function to continue executing where async started. Works perfect for me.
var tasks = [];
for(i=0;i<20;i++){
tasks.push(i); //can be replaced with list of steps, url and so on
}
var current = 0;
function doAjax(callback) {
//check to make sure there are more requests to make
if (current < tasks.length -1 ) {
var uploadURL ="http://localhost/someSequentialToDo";
//and
var myData = tasks[current];
current++;
//make the AJAX request with the given data
$.ajax({
type: 'GET',
url : uploadURL,
data: {index: current},
dataType : 'json',
success : function (serverResponse) {
doAjax(callback);
}
});
}
else
{
callback();
console.log("this is end");
}
}
function sth(){
var datum = Date();
doAjax( function(){
console.log(datum); //displays time when ajax started
console.log(Date()); //when ajax finished
});
}
console.log("start");
sth();
In the success callback function, just make another $.ajax request if necessary. (Setting async: false causes the browser to run the request as the same thread as everything else; that's why it freezes up.)
Use a callback function, there are two: success and error.
From the jQuery ajax page:
$.ajax({
url: "test.html",
context: document.body,
success: function(){
// Do processing, call function for next ajax
}
});
A (very) simplified example:
function doAjax() {
// get url and parameters
var myurl = /* somethingsomething */;
$.ajax({
url: myurl,
context: document.body,
success: function(data){
if(data < threshold) {
doAjax();
}
}
});
}
Try using $.when() (available since 1.5) you can have a single callback that triggers once all calls are made, its cleaner and much more elegant. It ends up looking something like this:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
// a1 and a2 are arguments resolved for the page1 and page2 ajax requests, respectively
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
alert( jqXHR.responseText )
});

Resources