This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
AJAX- response data not saved to global scope?
I basically have a loop that contacts a script on my site using AJAX and then updates a string with the response from that script. Here's the code:
// Image code array
var result_url = 'http://localhost/view/';
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
$.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
});
}
console.log(result_url);
The Firebug console just shows http://localhost/view/ when the string is logged. It's like the img_code response from my upload script isn't being appended to the string at all. I have tried logging the value of result_url within the $.post() method and that works fine, but the value is not being saved properly because it doesn't show later in my code. Is this a scope problem? Will I have to define result_url as a global variable?
Thanks for any help.
You are checking console.log(result_url); before the AJAX requests complete.
AJAX requests are (by default) run asynchronously. What that means is that your script continues to run while the request is still being made to the server.
Your callback function (provided to $.post as the 3rd parameter) is the one that get's executed after your AJAX request has completed.
Also note, that your AJAX request callback functions are called when the request is done. Your requests might not finish in the same order that they started. You could prevent all this by setting async:false, but that'll halt all of your javascript execution.
Another option would be to collect the jqXHR objects being returned by $.post, and then call $.when().done(), so that your console.log(result_url) happens only when all the AJAX requests are resolved:
// Image code array
var result_url = 'http://localhost/view/',
jqHXRs = [];
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
jqHXRs.push($.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
}));
}
$.when.apply(this, jqHXRs).done(function(){
console.log(result_url);
});
This is because you're doing the console.log immediately after firing the Ajax. Since Ajax is asynchronous, the success function will not necessarily be called before the code which follows your ajax code.
jQuery's Ajax tools provide a way of calling ajax synchronously by including the async:false option. Try replacing your ajax call with:
$.ajax({
url:'/upload',
data:{ url: rls[i], username:username },
success:function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
},
method:"post",
async:false
});
That way, code which follows you Ajax call would only be executed after the ajax completes.
Remember, though that this will lock up your page for the duration of the Ajax. Maybe it would be easier to just put the console.log(result_url); at the end of the success callback.
You're logging the result_url after the loop, but the $.post upload request may not have been complete yet. What I would recommend is to put the code that uses result_url inside a continuation call back and call it after you know that the last post request has completed.
e.g.
function continuation_code(result_url) {
// all your code that uses result_url goes here.
}
var result_url = 'http://localhost/view/';
var num_results_returned = 0;
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
$.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
num_results_returned += 1;
if (num_results_returned == urls.length) {
continuation_code(result_url);
}
});
}
Related
I have been using an AJAX query in my JavaScript code, and want my HTML & JavaScript code to pause loading until the results of the query are in. Is this possible? I believe async functions continue executing your code but make it possible to 'callback' to the function once it has completed a step. In my case I just want everything to stop, so nothng happening asynchronously.
For an example, please see my code below.
function ajaxQuerySpotify(stringIncrementalAmount) {
$.ajax({
url: 'https://api.spotify.com/v1/me/tracks?limit=50&offset=' + stringIncrementalAmount,
headers: {
'Authorization': 'Bearer ' + access_token
},
success: function(response) {
for (i = 0; i < response['items'].length; i++) {
songIDs.push(response['items'][i]['track']['id']);
songNames.push(response['items'][i]['track']['album']['artists'][0]['name']);
SongArtists.push(response['items'][i]['track']['name']);
}
requestCount += 1;
incrementalRequest = requestCount * incrementalAmount;
stringIncrementalAmount = String(incrementalRequest);
// console.log(stringIncrementalAmount);
// SET BACK TO 1000 ONCE COMPLETED TESTING
if (incrementalRequest >= 999) {
return false;
}
// Keeps requerying until nothing can be found anymore
ajaxQuerySpotify(stringIncrementalAmount);
}
})
}
I am using JQuery to chain ajax calls in a WordPress environment to hit a third-party API.
The workflow is such that I first get an array of objects each with a start_date and end_date. I then need to make ajax calls using those parameters and associate the response with the original params. I also need to do the ajax requests one by one because it isn't critical they all load right away and I want to control the network requests (there are a lot of other assets loading on the page).
Anyway, here is my code:
var superobjects; //variable to hold final result - want the response and the original date params in this array of objects
var callback = function(result) {
superobjects.push(result); //callback of ajax request using jQuery.when
};
var requests = []; // array to hold requests before doing them one at a time with jQuery.when
for (i = 0; i < data.length; i++) {
requests.push(jQuery.ajax({
url: wpApiSettings.root + 'superplugin/v1/superdooperendpoint/' + api_key + "/" + data[i].start_date + "/" + data[i].end_date,
method: 'GET',
beforeSend: function(xhr) {
// Set nonce here
xhr.setRequestHeader('X-WP-Nonce', wpApiSettings.nonce);
},
done: function(data) {
console.log({
start_date: data[i].start_date,
end_date: data[i].end_date
});
//this console.log above never fires
}
}));
}
setTimeout(function() {
console.log(superobjects) //no request information here!
}, 2222);
jQuery.when.apply(undefined, requests).then(function(result) {
callback(result) //run the ajax sequentially and then do the callback - problem is I've lost the request params here!
});
As you may be able to tell, when I do the superobjects in the log, it doesn't have any information from the start_date or end_date I used to create the request, just the result of the ajax call. Is there anyway to still have this sequential ajax workflow and still get the request params in my final superobject? I'm guessing there is with clever use of functions and scopes, but I don't see it right now so am looking for help! :)
I used a closure, I believe it does what I want. :)
var superobjects = [];
var success_callback = function(result) {
superobjects.push(result);
};
var requests = [];
console.log(data);
for (var i = 0; i < data.length; i++) {
(function(i, data) {
requests.push(jQuery.ajax({
url: wpApiSettings.root + 'superplugin/v1/superdooperendpoint/' + api_key + "/" + data[i].start_date + "/" + data[i].end_date,
method: 'GET',
beforeSend: function(xhr) {
// Set nonce here
xhr.setRequestHeader('X-WP-Nonce', wpApiSettings.nonce);
},
success: function(result) {
success_callback({
start_date: data[i].start_date,
end_date: data[i].end_date,
span: data[i].span,
result: result
});
}
}));
})(i, data);
};
setTimeout(function() {
console.log(superobjects)
}, 2222);
jQuery.when.apply(undefined, requests).then(function(result) {});
I've been working on getting a function written to:
1) Process an input array using $.ajax calls to fill an output array (below this is inputList)
2) Below is what I have, but I'm having issues with it:
requestData(), when I call it, runs straight through to processing the outputList array without having fully populated/filled it - it puts one value into it then starts to process that, but the function still apparently runs on seperately to the subsequent processing asynchronously. I need it to be fully synchronous so that it does not return until the inputList array has been fully processed.
I'm not seeing the browser repainting the div that has its html updated on every call of the runajax() function - I'm attempting to do this with a setTimeout.
3) I've set the ajax request to be synchronous (async : false) - but this doesn't seem to help
I've tried to use jQuery's $.when to provide an ability to ensure that everything gets called in sequence - but clearly I'm not doing this correctly.
Would appreciate any help - I've asked previous related questions around this and had some useful help - but I've still not resolved this!
Thanks
//declare holding function requestData - expects a non-empty input data array named inputList
function requestData() {
//declare inner function runajax
function runajax() {
if(inputList.length > 0) {
//get first item from inputlist and shorten inputList
var data = $.trim(inputList.shift());
function getData() {
//send the data to server
return $.ajax({
url: 'sada_ajax_fetch_data.php',
cache: false,
async: false,
method: "post",
timeout: 2000,
data: {
requesttype: "getmydata",
email: encodeURIComponent(data)
}
});
}
function handleReturnedData (response) {
response = $.trim(decodeURIComponent(response));
//update the div inner html
if(response == "Failed") {
$('#fetchupdatestatus').html('There was an error retrieving the data you requested!');
} else {
$('#fetchupdatestatus').html('The item returned was '+response);
}
//add the response from ajax to the end of the outputList array
outputList.push(response);
//set up the next ajax call
var doNextBitOfWork = function () {
runajax();
};
//call setTimeout so that browser shows refreshed div html
setTimeout(doNextBitOfWork, 0);
//return
return $.when();
}
//do the next ajax request and response processing
return getData().done(handleReturnedData);
} else {
//did the last one so return
return $.when();
}
}
//kick off the ajax calls
runajax();
}
var inputList = new Array();
var outputList = new Array();
.....load +/- 100 values to be processed using ajax into array inputList
requestData();
.....process stuff in array outputList
.....etc
There was my answer with "you're doing it wrong" earlier, but then I just decided to show, how you can do it (almost) right: https://jsfiddle.net/h4ffz1by/
var request_maker = {
working: false,
queue: [],
output: [],
requestData: function(inputList) {
if (request_maker.working == true) {
return false;
}
request_maker.output = [];
request_maker.working = true;
while (inputList.length > 0) {
var data = $.trim(inputList.shift());
request_maker.queue.push(data);
}
console.log(request_maker.queue);
request_maker.doTheJob();
return true;
},
doTheJob: function() {
current_data_to_send = request_maker.queue.shift();
console.log(current_data_to_send);
if (typeof current_data_to_send != 'undefined' && request_maker.queue.length >= 0) {
$.ajax({
url: '/echo/json/',
cache: false,
method: "post",
timeout: 2000,
data: {
requesttype: "getmydata",
email: encodeURIComponent(current_data_to_send)
},
success: function(data, status, xhrobject) {
console.log(xhrobject);
request_maker.handleReturnedData(data);
},
});
} else {
request_maker.working = false;
console.log('all data has been sent');
}
},
handleReturnedData: function(response) {
console.log(response);
response = $.trim(decodeURIComponent(response));
//response= 'Failed';//uncomment to emulate this kind of response
if (response == "Failed") {
$('#fetchupdatestatus').append('There was an error retrieving the data you requested!<br/>');
} else {
$('#fetchupdatestatus').append('The item returned was ' + response + '<br/>');
request_maker.output.push(response);
}
request_maker.doTheJob();
if (request_maker.working == false) {
console.log('all requests have been completed');
console.log(request_maker.output);
}
}
}
inputList = [1, 2, 3, 4, 5, 6];
if (request_maker.requestData(inputList)) {
console.log('started working');
}
if (!request_maker.requestData(inputList)) {
console.log('work in progress, try again later');
}
Note that I've changed request path to jsfiddle's ajax simulation link and replaced html() with append() calls to print text in div. The calls are made and get handled in the same order as it is in inputList, still they don't lock user's browser. request_maker.output's elements order is also the same as in inputList.
Have in mind, that you will need to add error handling too (probably just a function that pushes 'error' string into output instead of result), otherwise any ajax error (403/404/502, etc.) will get it "stuck" in working state. Or you can use complete instead of success and check request status right there.
UPD: Answer to the question: you cannot get both. You either use callbacks and let browser repaint inbetween asynchroneous requests or you make requests synchroneous and block browser untill your code finished working.
UPD2: There actually is some information on forcing redraw, however I don't know if it will work for you: Force DOM redraw/refresh on Chrome/Mac
Ive been struggling to pass my parameters from a functions but I just really can't figure out where did I go wrong. I have a function that have a parameters that I want to pass to my postData to display datas in my jQgrid. Here's my function code with parameters:
function getTID(hdrID){
var selected = $('#editTallyHdr').val();
var hdrID = '';
var hdrNo = '';
var nameFlag=0;
var par_ams = {
"SessionID": $.cookie("SessionID"),
"dataType": "data"
};
$.ajax({
type: 'GET',
url: 'processjson.php?' + $.param({path:'getData/tallyHdr',json:JSON.stringify(par_ams)}),
dataType: primeSettings.ajaxDataType,
success: function(data) {
if ('error' in data)
{
showMessage('ERROR: ' + data["error"]["msg"]);
}
else{
$.each(data['result']['main']['rowdata'], function(rowIndex, rowDataValue) {
$.each(rowDataValue, function(columnIndex, rowArrayValue) {
var fldName = data['result']['main']['metadata']['fields'][columnIndex].name;
if (fldName == 'transaction_id'){
hdrID = rowArrayValue;
}
if (fldName == 'transaction_num'){
hdrNo = rowArrayValue;
if(selected == hdrNo){
nameFlag =1;
};
}
});
});
}
}
});
return (hdrID);
}
and here is my jQgrid code where I call that function to get it's parameter:
$("#tblPlank").jqGrid({
url: '',
datatype: 'local',
jsonReader : {
.
.
.
serializeGridData: function(postData) {
var ctr =0;
var filt=[];
var c=[];
var jsonParams = {
'SessionID': $.cookie("SessionID"),
'dataType': 'data',
'transaction_id':getTID(hdrID),
'filters': c,
'lines':plank_data,
'recordLimit': postData.rows,
'recordOffset': postData.rows * (postData.page - 1),
'rowDataAsObjects': false,
'queryRowCount': true,
'sort_fields': postData.sidx
};
.
.// some code here
.
.
return 'json=' + JSON.stringify(jsonParams);
},
loadError: function(xhr, msg, e) {
showMessage('HTTP error: ' + JSON.stringify(msg) + '.');
},
colNames:[...],
colModel:[
........................
],
.
.
.
caption: "Tally Transaction Details/Lines"
I also have another code where I want to get that parameter. Here's the last code:
var par_ams = {
"SessionID": $.cookie("SessionID"),
"dataType": "data",
"transaction_id": getTID(hdrTID)
}
$('#tblPlank').setGridParam({
url:'processjson.php?path=' + encodeURI('getData/tallyLnDtl') + '&json=' + encodeURI(JSON.stringify(par_ams)),
datatype: primeSettings.ajaxDataType,
});
$('#tblPlank').trigger('reloadGrid');
Those codes below that function getTID(hdrID) cant retrieve the parameter, it shows empty. This maybe simple to anyone, but I really need help on this.. been working with this for quite long hours.
This is a very common misunderstanding. I've probably answered 15 of these questions in the last two weeks alone. An ajax call is an asynchronous call. That means that when you make the ajax call, it just STARTs the request. Then, while that request goes in the background, your code immediately keeps executing. That means that your function getTID() returns before the ajax call has even completed and it's value is not yet known. Thus, there is no way to return the response value from the ajax function when you return from getTID() as it is simply not known yet.
To work with asynchronous function calls (like ajax calls), you have to change your programming style to something that works asynchronously. In this case, the response to your ajax call is ONLY known in the success handler for the ajax all. So, you have to restructure your code to continue on with the execution of your processing and the handling of the ajax response from the success handler. If you have only a little bit of work to do, then you can put it all in the success handler. If you have a lot of work to do, then you can put all the rest of that work in a function call and call it from the success handler.
The problem is that you're doing an ajax-request (asynchronous request). Then the function does not wait for an answer to arrive, but just continues and returns hdrID (which isn't set at the time). After that a response comes in, and the success-method is called, which sets hdrID to the appropiate value.
The common way to solve this is to execute a specific function with the desired values when the success-method is executed. It's too much code to look into, but it could go something like this:
function fetchContent(continueFunction) {
$.ajax(params).success(function(reply) {
// retrieve desired params from reply
continueFunction(retrievedParameters);
}
}
What you could do is define getTID to take in a callback to execute once it has the id, for instance
function getTID(hdrID, callback) {
//ajax stuff....
success: function (data) {
// Error checks, etc
hdrID = //something dependent on data
callback(hdrID); // THIS IS THE IMPORTANT PART
}
the callback will execute after the request has returned, when it is safe to use the data returned from the ajax request that will be needed in the callback. You could wrap all of the code that needs the return value of the request in the callback, for example
getTID(hdrID, function (ID) {
var params = {
"SessionID": $.cookie("SessionID"),
"dataType": "data",
"transaction_id": ID //USE ID
}
$('#tblPlank').setGridParam({
url:'processjson.php?path=' + encodeURI('getData/tallyLnDtl') + '&json=' + encodeURI(JSON.stringify(par_ams)),
datatype: primeSettings.ajaxDataType,
});
$('#tblPlank').trigger('reloadGrid');
};
});
OK, so I cannot seem to be able to change the global variable of systemPath after it goes through the ajax.It will work inside of ajax, but I need that updated variable outside of ajax. basically I'm trying to create an array of paths from xml and use them to locate other xml files that I can generate a table from.
Does anyone know what's going on here? Does ajax run before the variable is set and that is why I get an array length of 0 after the ajax?
var systemPath = new Array();
var techDigestArr = new Array();
var addToArray = function(thisarray, toPush){
thisarray.push(toPush);
}
$.ajax({
url: fullPath+"technical/systems/systems.xml",
dataType: ($.browser.msie) ? "text" : "xml",
success: function(data){
var xml;
if (typeof data == "string") {
xml = new ActiveXObject("Microsoft.XMLDOM");
xml.async = false;
xml.loadXML(data);
} else {
xml = data;
}
$(xml).find("system").each(function(){
var urlString = fullPath + "technical/system_" + $(this).attr("id") + "/" + $(this).attr("id") + "tech-digest.xml <br />";
//alert(urlString);
$("#td-articles").append(systemPath.length + urlString);
addToArray(systemPath,urlString);
//systemPath.push(urlString);
});
$("#msg-output").append("total - " +systemPath.length);//Returns 48
},//END SUCCSESS
error: function(){
alert("Sorry - ");
history.go(-1);
}
});//END AJAX CALL
$(document).ready(function(){
//$("#msg-output").append("total - " + systemPath.length); Returns 0
});
The ajax is ran asynchronously. Things execute in this order in your code.
stuff before $.ajax()
$.ajax() initiates an ajax call (while waiting for the response it continues to run the rest of the code)
stuff after $.ajax()
success callback
Note that depending on how fast the call is 3 and 4 might occur in reverse order (not the case here)
So when $(document).ready() is executed the ajax call might not have returned yet, so the code in the success callback didn't have a chance to execute. If you are lucky and have a fast connection than maybe the response will come before document ready, but it's unlikely.
Just so you can see that the global variable gets updated you can set a timeout:
$(document).ready(function(){
setTimeout(function(){
$("#msg-output").append("total - " + systemPath.length);
//if the delay set below is more than the time between the ajax request and the server response than this will print the correct value
},2000);
});