I am using JSON to call a oracle service which fetches data (a list) from Server side. Inside the JSON call, I iterate through the list in a FOR loop and I need to call another web service to fetch some data related to each row in the list. I have written that logic in a JavaScript function, and I am calling that function inside the FOR loop.
The issue I am facing is, for example, if original list consists of a 2 items, then the FOR loop is executing 2 times first, and then the function is called 2 times together.
What I want is that every time the loop is iterated, the java script method should be called in the same iteration, fetch the values, I should be able to perform actions based on the results fetched, and it should be repeated for next iterations.
The javascript code is given below. Sequence of alert I am want is: 1,3,2,4,1,3,2,4,5.
Sequence of alert which I am getting is: 1,4,1,4,3,2,3,2,5
var url = "idcplg?IdcService=AJAX_SERVICE&Id=" + Id;
$.getJSON(escapeCachingForUrl(url), function(data){
for(var i=0;i<data.length;i++){
alert("1");
//some code written
myMethod(data,function(result){
alert("2");
// Need to operate on result
});
alert("4");
}
alert("5");
});
function myMethod(data,callback){
var url = "idcplg?IdcService=AJAX_ANOTHER_SERVICE";
$.getJSON(escapeCachingForUrl(url), {
data : data
}, function(data){
alert("3");
callback(data);
});
}
Related
This question already has answers here:
How do I return the accumulated results of multiple (parallel) asynchronous function calls in a loop?
(5 answers)
Closed 6 years ago.
I have this function
function add_cnh(arr_clelem){
var id=arr_clelem.getElementsByClassName("present")[0].id;
var date=arr_clelem.getElementsByClassName("present")[0].getAttribute('date');
var tt_entry= arr_clelem.getElementsByClassName("present")[0].getAttribute('tt_entry');
//new Ajax.Updater('register', '/some_url', { method: 'get' });
new Ajax.Request('/attendances/new',
{
parameters:'id='+id+'&date='+date+'&timetable_entry='+tt_entry+'&subject_id='+subject_id,
asynchronous:true,
evalScripts:true,
method:'get'
/*onSuccess: function(transport) {
var response = transport.responseText || "no response text";
alert("Success! \n\n" + response);
}*/
}
)
var ret=modal_data;
// $$('.MB_close').invoke('observe', 'click', _deinit);
return ret;
}
This function takes html-elements-object as an argument and basically render a modal-box and that modal box contain a form -elements which i need to store inside an array. The variable modal_data contains the elements which I require and its a global variable define in another file.
My problem is
This is a very old project using many JavaScript frameworks and libraries which date back to 2006 the library responsible for opening the model box itself is deprecated as can be seen here
And somehow I don't want to get into server side so I am using a for loop something like this
for(var i=0; i<arr_of_elements.length, i++)
{
my_arrvar[i]=add_cnh(arr_of_elements[i]);
}
Now with each itteration since I want the modal box to get closed and store the data within 'my_arrvar' which is somehow not possible as the call is asynchronous in nature and I've used closures and callbacks but no success. I don't want to use any sort of timer. So this is how it goes
Call the function and get data for each call and remove the modal box by id.
Also can this be used somehow and if then how?
You have to pass in ajax request asynchronous:false instead of true. Otherwise its not possible by another way.
Other Way using jQuery
The easiest way is to use the .ajaxStop() event handler:
$(document).ajaxStop(function() {
// place code to be executed on completion of last outstanding ajax call here
});
See jQuery Event handler
My script makes an Ajax request through jQuery. The data received is json_encoded via a server side script (php). This is further, stringified and then parseJSON'ed using jQuery to yield a recordCollection. I loop through recordCollection and push it onto records array, the variable that was declared outside the scope of the callback function.
The function looks like this:
var records = [] ;
$.getJSON(url, {id : recordid}, function(data) {
var recordCollection = jQuery.parseJSON(JSON.stringify(data));
$.each(recordCollection, function(){
records.push(this);
});
console.log(records) // displays all the objects, thus, the above code is right
});
console.log(records); // displays []
As you can notice above, the second time I logged records onto the console, it yielded an empty array, which is leading me to two conclusions:
Javascript arrays are passed by value, hence records outside the scope of the callback function does not retain it's value.
Ajax is asynchronous, hence records is being logged before the ajax call was complete and hence it still retains the value of the empty uninitialized records array and is logging that onto the console.
Now if 1 is true, how can I initialize the records array ?
And if 2 is true, should I make this Ajax call synchronous? This will make javascript block until the value is returned and thus the second time records array is logged onto the console, it will display the updated value of the array ?
The third is that I am completely missing a trick and doing something really dumb here.
I do need the records array to be set with the data returned from the ajax call as it needs to be passed around to different javascript functions in the script and the DOM basically waits for this result to load the data.
Thanks guys!
You are correct in that, ajax calls are async, hence Asynchronous Javascript and XML. You can make it sync, but you shouldn't because your array will then be available for anyone to play with, which can cause some big headaches.
Instead, have an init call that is run once your ajax call is completed.
function callback(records) {
// do stuff with records.
}
$.getJSON(url, {id : recordid}, function(data) {
var recordCollection = jQuery.parseJSON(JSON.stringify(data));
callback(recordCollection);
});
Ajax stands for Asynchronous JavaScript and XML (forget the XML part) so i think you can guess what is right ;-)
I added the execution order with comments:
var records = [] ;
// 1. send request and return immediately
$.getJSON(url, {id : recordid}, function(data) {
// 3. response from server is received
var recordCollection = jQuery.parseJSON(JSON.stringify(data));
$.each(recordCollection, function(){
records.push(this);
});
console.log(records)
});
// 2. print records
console.log(records);
You should not try to make the request synchronous in any way. If you need to do something with records after the data is available you should create a new function and call it after you pushed the values into records.
I am attempting to create an array of objects and then access object properties within the array, but it comes back undefined. I call the createObjArray() function and immediately after I do a console.log(objArray[1]); and it prints out the object with all it...s properties just fine. However, if I attempt to do console.log(objArray[1].name); firebug prints "undefined". Also, when stepping through my code in firebug I can mouse over objArray[1].name and it displays the correct name. What is happening here, it's driving me nuts.
var objArray = [];
function createObjectArray(numOfObjs) {
for(var i=0; i<numOfObjs; i++) {
packages.push(initObj(i));
}
}
function initObj(i){
var newPackage;
var p = {};
$.getJSON('.../package' + i + '.json', function(data) {
newPackage = new Package(data);
p.name = newPackage.name;
p.id = i;
});
return p;
}
This will work:
var objArray = [];
function createObjectArray(numOfObjs, callback) {
var filledPackage = [];
var nbLeft = numOfObjs;
for(var i=0; i<numOfObjs; i++) {
initObj(i, function(p){
filledPackage.push(p);
nbLeft--;
if (nbLeft === 0){
callback(filledPackage);
}
});
}
}
function initObj(i, callback){
var newPackage;
var p = {};
$.getJSON('.../package' + i + '.json', function(data) {
newPackage = new Package(data);
p.name = newPackage.name;
p.id = i;
callback(p);
});
}
//Get a filled object array:
createObjectArray(5, function(filledArray){
objArray = filledArray;
//Code here will be executed AFTER all the $.getJSON queries have returned.
//objArray is not empty.
});
//Code here will be executed WHILE the getJSON queries are running and
//while objArray is still empty. Due to the way the JS event loop works,
//it is impossible that code placed here will be able to use the content
//of objArray unless you call an async function such as anything AJAX or
//setTimeout, but that's iffy. Code you want to be executed once objArray
//has been filled should be inside of the callback above.
The problem is that $.getJSON is aynchronous, meaning that it doesn't automatically returns a result. Instead, you give it a callback. A callback is a function to execute once it has received a result. In this case, the callback is the anonymous function created when calling $.getJSON. That callback receives the result from the server, adds it to the array and then checks if the array has been filled. Since we're doing async code due to the $.getJSON function, we must return the result asynchronously too. To do so, we demand the initObj function to receive a function to call once it has completed (another callback). We call that callback and pass it the parameter. We then return the filled array through a callback once again.
Your call to $.getJSON is asynchronous. When initObj() returns p it is still an empty object.
However initObj() creates a closure which captures a reference to p so when $.getJSON returns p is populated.
This is why the object seems empty in code you run immediately after populating the array. However by the time you run your console command the asynchronous calls have returned and the objects are populated.
You need to wait for all your async calls to return before continuing work on the array. One way to do this would be to increment a counter when you make a call and decrement it when a call returns, then when the final call returns the counter would drop to zero and you continue processing.
Alternatively you could setup a setTimout loop to keep polling the array the check when all its items are populated.
Both approaches are risky if you think one of the calls might fail, but the approach itself is fundamentally risky as you are making multiple ajax calls so you have to handle multiple possible failures. It would be a lot cleaner to grab all the data in one go so you can handle success / error states once in the success / error handler in jQuery.ajax.
I use callbacks all the time when using 3rd part libraries such as jquery, however I've run into an issue where I need to set up my own call back. Take for instance my current code:
// Get All Rates
function getAllRates() {
$('#shiplist tr.mainRow').each(function() {
var curid = $(this).attr("id");
var cursplit = curid.split("_");
var shipid = cursplit[1];
getRate(shipid);
});
}
This iterates through all the table rows that have class "mainRow", and each main row has the id of "shipment_#####" so for each row it splits by the _ to get the actual ship id, which it then uses to calla "getRate" function which sends an ajax call to the server using UPS or USPS api's to get the rate for the given shipment.
This works fine for UPS all rows start loading at once and return their rates independently. The problem is now with Stamps.com, they use an authenticated soap conversation so an authenticator string needs to be received from the first call and used in the next call and so on.
So basically I need to modify the above function to execute the "getRate()" function and wait for it to complete before executing the next iteration.
Anyone know how I can do this with out a lot of fuss?
EDIT - Clearification on the question being asked:
I want to take the following function:
getRate(shipid);
and access it like so:
getRate(shipid, function(shipList) { _Function_data_here_ });
When I define the getRate function, how do I define it so that it has that callback ability? how does that work?
I would suggest you extract all the IDs at once and pass them to your function. That way, you can easily process the first on individually and the rest afterwards. With deferred objects, this is really easy.
For example:
function extractAllIds(callback) {
var ids = $('#shiplist tr.mainRow').map(function() {
return this.id.split('_')[1];
}).get();
callback(ids);
}
function getRates(ids) {
var first = ids.shift();
$.ajax({data: first, ...}).done(function(response) {
// extract the authenticator string
for(var i = ids.length; i--; ) {
$.ajax({data: ids[i], ...});
}
});
}
extractAllIds(getRates);
In any way, you cannot make $.each wait for response of Ajax calls.
I have a need to collect data from multiple sources (xml or JSON format) via AJAX, and then run a function that consumes these data sets.
How can I check that all the AJAX calls are successful before the function is executed? Ideally, I'd like to keep async calls to minimize the response time.
Conceptually, it would work like this:
Use async calls.
Code a success handler for each call.
When a call succeeds, you store the data from it in some common place and then record that this call has finished.
At the end of each success handler, you check to see if all data is available now and, if so, you can a function to consume it.
Some pseudo code for an ajax call:
results = [];
$.ajax({
// other parameters here
success: function(data) {
results.push(data);
checkResults();
};
});
function checkResults() {
// if all five results are in, then we're ready to process them
if (results.length >= 5) {
processResults(results);
}
}