I have the following code, in a prototype and I would like to pass in the callback functions (successf, failuref) from where the instance of Data is created. This does not seem to get called though, any help appreciated.
It all works fine if defined in the main application obviously and if I use async: false it also works but I would like to do asynchronous...
Callbacks are declared as follows,
function bdsuccess( data, textStatus, jQxhr ){
...
};
function bdfailure( jqXhr, textStatus, errorThrown ){
...
};
//invocation...
var pd = new Data();
pd.getdata('/resolve', 'test', bdsuccess, bdfailure);
Prototype is as below...
function Data() {
}
Data.prototype.getdata = function(route, req, successf, failuref) {
var ps = new Support();
var bddata = ps.b64enc(req);
var res;
$.ajax({
url: route,
type: 'POST',
contentType: false,
async: false,
data: {bd: bddata},
success: successf,
error: failuref,
});
return res;
}
I'm trying to guess what you want - it seems like you want to set the callbacks in the "constructor" of Data
function Data(successf, failuref) {
this.successf = successf;
this.failuref = failuref;
}
Data.prototype.getdata = function(route, req) {
var ps = new Support();
var bddata = ps.b64enc(req);
var res;
$.ajax({
url: route,
type: 'POST',
contentType: false,
async: false,
data: {bd: bddata},
success: this.successf,
error: this.failuref,
});
return res;
}
function bdsuccess( data, textStatus, jQxhr ){
...
};
function bdfailure( jqXhr, textStatus, errorThrown ){
...
};
//invocation...
var pd = new Data(bdsuccess, bdfailure);
pd.getdata('/resolve', 'test');
Though there's two things I don't like about your original code
async: false
besides synchronous XHR on the main thread being deprecated, you're using callbacks, so why the synchronous call?
var res;
......
return res;
res is never assigned a value, so, what's the point?
I resolved this as follows,
in main.js file
var pd = new Data(Document);
pd.getdata('/resolve', 'testdata', bdsuccess, bdfailure);
function bdsuccess(data){
//success
};
function bdfailure(error){
/failure
};
in data.js file
function Data(type) {
this.subscribertype = type;
}
Data.prototype.getdata = function(route, req, successf, failuref) {
var doc = this.subscribertype;
var ps = new Support();
var bddata = ps.b64enc(req);
$.ajax({
url: route,
type: 'POST',
contentType: false,
data: {bd: bddata},
success: function( data, textStatus, jQxhr ){
successf.call(doc, data);
},
error: function( jqXhr, textStatus, errorThrown ){
failuref.call(doc, errorThrown);
}
});
}
Related
I have a function that loops through an array and calls a php script for each object of the array. The php script is getting the Lat/Lng from google maps api so i need to throttle the api calls. The list can be long (currently 300). I want to update the user as the script is running.
In my success callback I can console log every response but the element I want to update for the user only gets updated once.
Is there a way to update the user after each successful response?
here is my function:
$('#agents-lat-lng').click(function() {
var data = {
'action': 'getLatLng'
}
$.ajax({
url: ajax_url,
type: 'post',
data: data,
dataType: 'json',
success: function(response){
for ( var i = 0, l = response.query.length; i < l; i++ ) {
$.when(updateLatLngDB(response.query[i]))
.done(function(dataFromResponses){
console.log('1 ', dataFromResponses[0])
}).fail(function(response){
console.error(response);
});
}
},
error: function (textStatus, errorThrown) {
console.log(textStatus, errorThrown)
}
})
})
Here is the second function:
function updateLatLngDB(query) {
var deferred = new $.Deferred()
var dataFromResponses = []
var count = 0
var data = {
'action': 'updateLatLng',
'agent': query
}
$.ajax({
url: ajax_url,
type: 'post',
data: data,
dataType: 'json',
cache: false,
async: false,
success: function(response){
count++
console.log('count ', count)
$('#process-upload-response').text(count)
dataFromResponses.push(response)
},
error: function (textStatus, errorThrown) {
console.log(textStatus, errorThrown)
deferred.reject(response)
$('#process-upload-response').html(textStatus, errorThrown)
}
})
deferred.resolve(dataFromResponses)
return deferred.promise()
}
I have tried everything from Deferred to setTimout but I can not get #process-upload-response to update as each item in the array gets processed. It only updates after all the responses are complete.
UPDATE: I have been able to make it work by adding this to the for loop
(function(i){
window.setTimeout(function(){
promises.push(updateLatLngDB(response.query[i],response.query.length));
}, i * 1000);
}(i));
The problem is that you don't keep state between calls to updateLatLngDB function and also you resolve the value with empty array because you don't call the resolve outside of async call.
If I understand this is what you want:
$.ajax({
url: ajax_url,
type: 'post',
data: data,
dataType: 'json',
success: function(response){
var promises = [];
var updateLatLngDB = countRequests(0);
for ( var i = 0, l = response.query.length; i < l; i++ ) {
// collect promises
promises.push(updateLatLngDB(response.query[i]));
}
// when all request are done execute done
$.when.apply($, promises).done(function() {
var dataFromResponses = arguments;
console.log('1 ', dataFromResponses[0])
}).fail(function(response){
console.error(response);
});
},
error: function (textStatus, errorThrown) {
console.log(textStatus, errorThrown)
}
})
// keep count in closure
function countRequests(count) {
return function updateLatLngDB(query) {
var deferred = new $.Deferred();
var data = {
'action': 'updateLatLng',
'agent': query
}
$.ajax({
url: ajax_url,
type: 'post',
data: data,
dataType: 'json',
cache: false,
async: false,
success: function(response){
count++
console.log('count ', count)
$('#process-upload-response').text(count)
dataFromResponses.push(response);
deferred.resolve(response);
},
error: function (textStatus, errorThrown) {
console.log(textStatus, errorThrown)
deferred.reject(response)
$('#process-upload-response').html(textStatus, errorThrown)
}
})
return deferred.promise()
};
}
NOTE: that if you don't want all request to be done in parallel you will need to create async loop I like to write them like this (with async/await it's simpler because you can create normal while loop instead):
var promises = [];
var i = 0; = response.query.length;
(function loop() {
if (i < len) {
var promise = updateLatLngDB(response.query[i++]);
promises.push(promise);
promise.then(loop);
} else {
$.when.apply($, promises).done(function() {
var dataFromResponses = arguments;
console.log('1 ', dataFromResponses[0])
}).fail(function(response){
console.error(response);
});
}
})();
with this you will be able to delay each request (if this is what you want).
I have some js for which I want to write unit test cases using sinon. My js looks like following
var succesHandler = function() {
console.log("yeah success");
};
var failureHandler = function() {
console.log("oops error");
};
var ajaxCall = function(payload, successHandler, failureHandler) {
$.ajax({
type: "POST",
url: "/myUrl",
contentType: 'application/json',
data: JSON.stringify(payload),
success: successHandler,
error: failureHandler
});
};
$('#my-button').live('click', function() {
var dummyPayload = {
"hello" : "coder"
};
ajaxCall(dummyPayload, successHandler, failureHandler);
});
return {
ajaxCall: ajaxCall,
successHandler: successHandler,
failureHandler: failureHandler
};
I want to know how do I spy the ajaxCall() method attached to the #my-button click? Also, how to spy successHandler and failureHandler
I suddenly started getting the following:
TypeError: $.ajax(...).done(...).fail(...).complete is not a function
My code:
this.sendRequest = function (type, extension, data, successCallback, successMsg, failMsg, failCallback) {
var self = this;
var options = {
url: self.baseUrl + self.apiEndpoint + extension,
type: type,
dataType: 'json',
xhrFields: {
withCredentials: true
}
};
if (data != null) {
options.data = data;
}
return $.ajax(options)
.done(function (response) {
// do stuff
})
.fail(function (response) {
// do stuff
}).complete(function (response) {
// do stuff
});
};
Why is this happening? I did update jQuery - did some of this syntax become invalidated?
.complete is deprecated....use .always
jqXHR.always(function( data|jqXHR, textStatus, jqXHR|errorThrown ) { }); (added in jQuery 1.6)
An alternative construct to the complete callback option, the .always() method replaces the deprecated .complete() method.
i have an issue and i need an idea for solve :)
I have 2 call to $.ajax
First, is asynch, and during a lot of time (1 minutes for example)
Second, is sync (in ajax async: false) and it response fast (5 sec for example)
Second call is in a loop (requests->response->print data, request->response->print data).
I need when first finish (success or error), stop second call.
I attach an example code:
var success = false;
$.ajax({
type: "POST",
url: urlRest,
data: {
data: dataSend
},
success: processOK,
error: processError
});
do {
$.ajax({
type: "POST",
url: urlData,
data: {
data: dataSend
},
async: false,
success: function(data, textStatus, jqXHR){
console.log(data);
},
error: function(data, textStatus, jqXHR){
console.log("Error");
}
});
} while (!success);
I hope it's clear :)
I corrected an issue that would cause some errors, try this out.
let printData = function( input ){
let config = {
urlRest: '',
data: { data: {} },
loop: false,
callback: false
}
$.each(config,function(k,v){ config[k] = input[k] });
config.loop = false;
$.ajax({
type: 'POST',
url: config.urlRest,
data: config.data,
success: function( data ){
// Based on the response if you need to run again change config.loop to true and it will run again
// you can also alter anything your sending through
if( config.loop ) printData( config );
else if( typeof config.callback === 'function' ) callback();
},
error: function(){
// Based on the response if you need to run again change config.loop to true and it will run again
// you can also alter anything your sending through
if( config.loop ) printData( config );
else if( typeof config.callback === 'function' ) callback();
}
});
}
printData({
urlRest: '', // URL Here
data: data, // Data Object
loop: true, // Set this to true if you want it to loop
callback: function(){
console.log( 'Job Complete' );
}
})
You can run async calls in synchronous manner using SynJS:
function ajaxWrapper(ctx, url, data){
var res={done:false};
$.ajax({
type: "POST",
url: url,
data: data,
success: function(result){
res.data=result;
},
error: function(){
res.error=true;
},
}).always(function(){
res.done = true;
SynJS.resume(ctx); // <-- tell caller that callback is finished
});
return res; // <-- return object that will hold the results
}
// function that is executed in synchronous manner
function myFunc(modules, urlRest, urlData) {
var success = false;
var res1 = modules.ajaxWrapper(_synjsContext, urlRest, urlData);
SynJS.wait(res1.done); // <-- wait for result from callback
do {
var res2 = modules.ajaxWrapper(_synjsContext, urlRest, urlData);
SynJS.wait(res2.done); // <-- wait for result from 2nd callback
} while (!success);
}
var modules = {ajaxWrapper: ajaxWrapper};
SynJS.run(myFunc,null, modules, "/", {}, function () {
console.log('done');
});
You can change the success value like this
$.ajax({
type: "POST",
url: urlRest,
data: {
data: dataSend
}
}).always(function() {success=true;});
Or you can create a self call function (after the second ajax finish, calls it again) but before the call its checks the success variable like #mplungjan did.
It is never a good idea to loop Ajax. You need to allow the call to return.
Here is an example that is NOT using async false
var firstDone = false,tId;
// call long ajax
$.ajax({
type: "POST",
url: urlRest,
data: {
data: dataSend
}
}).done(processOK);
}).fail(processError)
}).always(function() {firstDone=true; clearTimeout(tId);}); // stops the other loop
// setup function that can be looped
function callAjax() {
if (firstDone) return;
$.ajax({
type: "POST",
url: urlData,
data: {
data: dataSend
}
}).done(function(data, textStatus, jqXHR) {
console.log(data);
}).fail(function(data, textStatus, jqXHR) {
console.log("Error");
}).always(function() {
tId=setTimeout(callAjax,1000); // give the server time to recover
});
}
callAjax();
I am loading 2 XML documents that both run functions on success, although the function for the 2nd XML document is dependant on the 1st being complete.
If I have async:true:
1st XML
function XmlDataTypes() {
var result = null;
var scriptUrl = "http://domain.com/xml/test.XmlDataTypes?AccountId=" + AccountId;
$.ajax(
{
url: scriptUrl,
type: 'get',
dataType: 'xml',
async: true,
success: function (data) {
//create array to be used in second XML
for (var i = 0; i < xmlRows.length; i++) {
var dataType = xmlRows[i];
var dataTypeId = nodeValue(dataType.getElementsByTagName("DataTypeId")[0]);
var dataTypeName = nodeValue(dataType.getElementsByTagName("DataTypeName")[0]);
dataTypeArray.push({ dataTypeId: dataTypeId, dataTypeName: dataTypeName, position: i, markerArray: [] });
}
},
error: function onXmlError() {
alert("An Error has occurred.");
}
});
return result;
}
2nd XML
function XmlAmenityData() {
var result = null;
var scriptUrl = "http://domain.com/xml/test.XmlAmenityData?AccountId=" + AccountId;
$.ajax(
{
url: scriptUrl,
type: 'get',
dataType: 'xml',
async: true,
success: function (data) {
//store this info in markerArray in dataTypeArray
},
error: function onXmlError() {
alert("An Error has occurred.");
}
});
return result;
}
The XML data can loaded in a random order so the function for the second document will error if the 1st hasn't completed.
If I set:
async: false
It works correctly but I get a warning:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.
Is there a way around this without using:
async: false
Since the 2nd xml is dependent on the 1st, you can define a callback on success.
Also since ajax is async, you must assign the result when the callback is called. You can define a variable ourside of your function (in this case an array) and put the data there.
var result = [];
function XmlDataTypes(url, accountId, callback) {
var scriptUrl = url + accountId;
$.ajax({
url: scriptUrl,
type: 'get',
dataType: 'xml',
async: true,
success: function (data) {
// do something
result.push(data);
if(typeof callback == 'function') {
callback();
}
},
error: function onXmlError() {
alert("An Error has occurred.");
}
});
}
function doSomething() {
// Do something to store this info in markerArray in dataTypeArray
// XmlAmenityData is in results var.
}
And you can use it like so
var _callback = XmlDataTypes("http://domain.com/xml/test.XmlAmenityData?AccountId=", "1234", doSomething);
XmlDataTypes("http://domain.com/xml/test.XmlDataTypes?AccountId=", "1234", _callback);
EDIT: Updated script based on given scenario.
You could try to return the $.ajax as a promise:
function XmlDataTypes() {
// note domain.com was changes to example.com - this should be changed back
var scriptUrl = "http://example.com/xml/test.XmlDataTypes?AccountId=" + AccountId;
return $.ajax(
{
url: scriptUrl,
type: 'get',
dataType: 'xml',
async: true,
success: function (data) {
//create array to be used in second XML
for (var i = 0; i < xmlRows.length; i++) {
var dataType = xmlRows[i];
var dataTypeId = nodeValue(dataType.getElementsByTagName("DataTypeId")[0]);
var dataTypeName = nodeValue(dataType.getElementsByTagName("DataTypeName")[0]);
dataTypeArray.push({ dataTypeId: dataTypeId, dataTypeName: dataTypeName, position: i, markerArray: [] });
}
},
error: function onXmlError() {
alert("An Error has occurred.");
}
});
}
Then calling them in sequence :
XmlDataTypes.done(XmlAmenityData);
Here is some more documentation :
http://www.htmlgoodies.com/beyond/javascript/making-promises-with-jquery-deferred.html