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
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).
The alert at the start shows "undefined", why?
The alerts come in this order:
"success!"
"Data" (what it should be)
"undefined"
I read through multiple threads, the problem was always that ajax was asynchronous, so the data was not defined when it was accessed, but in my case the data is there, the alert in my function shows the data BEFORE the other alert where it is undefined!
Very grateful for any help!
I got this code
var data = getData("");
alert(data); <<<<<<< UNDEFINED
function getData(fileName) {
$.ajax({
async:false,
type: "GET",
url: "breastCancer.csv",
dataType: "text",
success: function (data) {
var arrData = processData(data);
alert("success!");
alert(arrData); <<<<< WORKS GREAT
return arrData;
},
});
}
function processData(data) {
var arrData = CSVToArray(data);
dimensions = arrData[0];
var objects = [];
objects[0] = dimensions;
for (var i = 1; i < arrData.length; i++){
objects[i] = new Object();
for (var j = 0; j < dimensions.length; j++){
objects[i][dimensions[j]] = arrData[i][j];
}
}
return objects;
}
To clarify, I know asynchronous is the way to go for user experience, but this page just has to show data from this call, so its okay for me to wait for it.
Your getData function doesn't return anything.
You need to return it from the function itself.
function getData(fileName) {
$.ajax({
async:false,
type: "GET",
url: "breastCancer.csv",
dataType: "text",
success: function (data) {
var arrData = processData(data);
alert("success!");
alert(arrData); <<<<< WORKS GREAT
return arrData;
},
});
}
^ This returns the data within getData. But getData doesn't do anything with it: such as returning it.
function getData(fileName) {
var ourData = "";
$.ajax({
async:false,
type: "GET",
url: "breastCancer.csv",
dataType: "text",
success: function (data) {
var arrData = processData(data);
ourData = arrData;
},
});
return ourData;
}
This returns the data from getData to whatever calls that function.
edit: also, don't use async:false. Your browser won't capture any events happening until that AJAX completes. The benefit of asynchronous JS is that...we can! And in this case should.
Preface: Don't use async: false. But answering the question:
getData doesn't return anything. You're doing a return from the success callback, but that returns something from the success callback, not getData.
To change it so getData returns something, you'd do this:
function getData(fileName) {
var arrData;
$.ajax({
async:false,
type: "GET",
url: "breastCancer.csv",
dataType: "text",
success: function (data) {
arrData = processData(data);
},
});
return arrData; // <=== Now `getData` returns something
}
But don't do that. Instead, embrace asynchronous programming and remove async: false. For instance, a callback:
function getData(fileName) {
$.ajax({
async:false,
type: "GET",
url: "breastCancer.csv",
dataType: "text",
success: function (data) {
callback(processData(data));
},
});
}
...called like this:
getData(function(data) {
alert(data);
});
...or a promise ($.ajax returns one, of sorts):
function getData(fileName) {
return $.ajax({
async:false,
type: "GET",
url: "breastCancer.csv",
dataType: "text",
success: function (data) {
callback(processData(data));
},
}).then(data) {
return processData(data); // <== Becomes the resolution value of `getData`'s promise
});
}
and then
getData().then(function(data) {
alert(data);
});
data is undefined because the function getData doesn't return anything. You should have a look at promises.
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 have a problem with ajax post function. Ajax function doesn't working without alert message. If add alert message, div content will be refresh and working my code.
I have a mv3 application. And post form data to this javascript function. Like that:
function Add_Definition() {
var data = $.extend({ call: "Add_Definition", url: "/Definition/Definition", type: "post", dataType: "html", data: $("#definition_form").serialize() }, data);
Load(ajaxrequest(data));
}
function Load(val) {
if (val == true) {
var data = $.extend({ call: "Load", render: $("#render"), url: '#Url.Action("Definition", "Definition")', type: "get", dataType: "html", data: { "groupid": '#Request.QueryString["groupid"].ToString()'} }, data);
ajaxrequest(data);
}
}
My ajax javascript function is below.
function ajaxrequest(param) {
var result = true;
//alert("hi there");
$.ajax({
type: param.type,
url: param.url,
data: param.data,
dataType: param.dataType,
success: function (result) {
$(param.render).empty();
$(param.render).html(result);
result = true;
},
error: function (request, status, error) {
result= false;
}
});
return result; }
If remove // char from javascript code, it will not work, (need refresh page), if have a alert message, content will be refresh with load function.
How is error?
you can also direct call like
var divElem = $('#yourdivid').load('#Url.Action("Definition", "Definition")');
I have the following controller method:
public JsonResult CreateGroup(String GroupName)
{
ApplicationUser user;
var userName = User.Identity.Name;
using (DAL.GDContext context = new DAL.GDContext())
{
user = context.Users.FirstOrDefault(u => u.UserName == userName);
if (user != null)
{
var group = new Group();
group.GroupName = GroupName;
group.Members.Add(user);
context.Groups.Add(group);
context.SaveChanges();
}
}
string result = userName;
return Json(result, JsonRequestBehavior.AllowGet);
}
with the following ajax call:
$(function () {
$('#CreateGroup').on("click", function () {
var groupName = $('#groupname').val();
if (groupName != '') {
$.ajax({
url: '#Url.Action("CreateGroup","AjaxMethods")',
type: "POST",
data: JSON.stringify({ 'GroupName': groupName }),
dataType: "json",
cache: false,
contentType: "application/json; charset=utf-8",
success: function (data) {
alert("success");
CreateGroup(data);
},
error: function () {
alert("An error has occured!!!");
}
});
}
});
The CreateGroup function fails saying "Uncaught ReferenceError: data is not defined"
Do i have to use another Json request - type post - to get the username?
you can do the call without using JSON.stringify. Also your controller method has a cache attribute that may yield more control. Personally, I would use the controller cache control. You are probably getting a cached version of the controller call prior to returning data.
[OutputCache(NoStore = true, Duration = 0)]
public ActionResult CreateGroup(string GroupName)
$.ajax({
url: '#Url.Action("CreateGroup","AjaxMethods")',
type: "POST",
data: { 'GroupName': groupName },
dataType: "json",
traditional: true,
success: function (data, status, xhr ) {
alert("success");
CreateGroup(data);
},
error: function () {
alert("An error has occured!!!");
}
});
NOTE: Update success callback.