Update element on every Ajax success response - javascript

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).

Related

Ajax with callback passed

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);
}
});
}

jQuery AJAX stop request after another response

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();

complete callback is not being called for async.parallel

I need to make an api call for 100 rows to populate description (which I prefer to do it in parallel). However some of rows might not have description in this case api will return 404. I need to show a warning message when there are a row or rows without description and remove those rows from modal data which means I need a complete callback or done callback. However the completeCallback is not being called and I "think" it's because some of rows doesn't have description.
Could you please tell me how to achieve that?
Here is my code:
function getDescription(processedData) {
$.ajax({
url: baseApiUrl + '/Summary?id=' + processedData.id,
type: 'GET',
dataType: 'json',
contentType: 'application/json',
success: function (data) {
processedData.SummaryDescription = data;
},
error: function (xhr, status, e) {
if(xhr.statusCode === 404){
processedData.SummaryDescription = '';
}else{
}
}
});
};
//Below line is in a look
parallelCallArray.push(getDescription.bind(null, processedData));
//Below line is out of loop
Async.parallel(parallelCallArray, function(err, result){
console.log('all calls completed...');
});
You're missing the callback parameter of your function(s) that are being executed in parallel. If you don't execute the callback, async will assume your functions haven't finished yet. Try something like this:
function getDescription(processedData, cb) {
$.ajax({
url: baseApiUrl + '/Summary?id=' + processedData.id,
type: 'GET',
dataType: 'json',
contentType: 'application/json',
success: function (data) {
processedData.SummaryDescription = data;
cb();
},
error: function (xhr, status, e) {
if (xhr.statusCode === 404) {
processedData.SummaryDescription = '';
} else {
}
cb(new Error(e));
}
});
}

ajax loading 2 XML documents in order without async:false

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

jQuery AJAX custom function and custom callback?

Heylow everyone!
I have an ajax() call like so:
$.ajax({
type: "post",
url: "whatever.php",
data: {
theData: "moo moo"
},
success: function(data) {
console.log(data);
}
});
Is it possible to wrap this inside a custom function but retain the callback?
Something like:
function customAjax(u, d, theCallbackStuff) {
$.ajax({
type: "post",
url: u,
data: d,
success: function(data) {
//RUN theCallbackStuff
}
});
}
theCallbackStuff will be something like:
var m = 1;
var n = 2;
alert(m + n + data);
EDIT:
Got a recent upvote for this and I feel compelled to state that I would no longer do it this way. $.ajax returns a promise so you can do pretty much what i just did here in a more consistent and robust way using the promise directly.
function customRequest(u,d) {
var promise = $.ajax({
type: 'post',
data: d,
url: u
})
.done(function (responseData, status, xhr) {
// preconfigured logic for success
})
.fail(function (xhr, status, err) {
//predetermined logic for unsuccessful request
});
return promise;
}
Then usage looks like:
// using `done` which will add the callback to the stack
// to be run when the promise is resolved
customRequest('whatever.php', {'somekey': 'somevalue'}).done(function (data) {
var n = 1,
m = 2;
alert(m + n + data);
});
// using fail which will add the callback to the stack
// to be run when the promise is rejected
customRequest('whatever.php', {'somekey': 'somevalue'}).fail(function (xhr, status, err) {
console.log(status, err);
});
// using then which will add callabcks to the
// success AND failure stacks respectively when
// the request is resolved/rejected
customRequest('whatever.php', {'somekey': 'somevalue'}).then(
function (data) {
var n = 1,
m = 2;
alert(m + n + data);
},
function (xhr, status, err) {
console.log(status, err);
});
Sure i do this all the time. You can either execute the callback within the actual success callack or you can assign the callback as the success callback:
function customRequest(u,d,callback) {
$.ajax({
type: "post",
url: u,
data:d,
success: function(data) {
console.log(data); // predefined logic if any
if(typeof callback == 'function') {
callback(data);
}
}
});
}
Usage would look something like:
customRequest('whatever.php', {'somekey': 'somevalue'}, function (data) {
var n = 1,
m = 2;
alert(m + n + data);
});
function customAjax(u, d, theCallbackStuff) {
$.ajax({
type: "post",
url: u,
data: d,
success: theCallbackStuff
});
}
customAjax(url, data, function(data){
//do something
});
On this note, you can pass a complete function as a callback to this:
function customRequest(u,d,callback) {
$.ajax({
type: "post",
url: u,
data:d,
success: function(data) {
console.log(data); // predefined logic if any
if(typeof callback == 'function') {
callback(data);
}
}
});
}
// Then call it as follows:
function initiator() {
customRequest( '/url/to/post', 'param1=val', function() { alert( 'complete' ); })
}
Simply passing it as an anonymous function will work too.. Just for the sake of showing :)

Categories