Use of async on successive AJAX calls - javascript

Question here about use of successive AJAX calls and async. Its a bit messed here because of how the data is set up. I need to return listings, but the sever only returns 10 per query, and the only way to determine the total number of listings is a separate query with the boolean returnTotal as true instead of false. This returns the number of listings only, and not the listing results themselves. However, if I run the calls synchronously, the variable startItem (which increments on each loop to load data starting at the next block of listings) doesn't seem to populate before the next call finishes, and results get duplicated. Any way to avoid running both as async? Apologies if my code is batshit ridiculous, as I'm relatively new to jquery.
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem=0&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=true",
dataType: "html",
async: false,
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
$(this).find('total').each(function(){
totalList = $(this).text();
totalList = parseInt(totalList);
totalPages = totalList/10;
});
});
},
});
for (i = 0; i < totalPages; i++){
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
startItem = startItem + 10;
popResults();
},
});
}

The problem here is that you do not increment startItem until you receive a response. Your code is probably making multiple requests with startItem === 1 before the first response is even received, and so you will get some really weird behavior (probably will get duplicate responses, and you will only get the first few pages of data).
Avoid using synchronous calls because they will tie up other resources (like javascript).
In this case if you want to insure that you get the data in order, you can make it a serial chain of AJAX calls.
To get serial behavior and enjoy the benefits of AJAX, instead of using a loop make your callback function do the next AJAX request after incrementing startItem.
This is easier if you organize your code into functions. To wit:
function GetData()
{
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: GetData_Callback
});
}
function GetData_Callback(data)
{
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
startItem += 10; // increment startItem
popResults();
if (startItem / 10 < totalPages)
{
GetData(); // get next "page" of data
}
}
var startItem = 1; // global variable will be mutated by GetData_Callback
GetData(); // get first "page" of data
To do this in parallel typically requires management of the parallel responses (you can use semaphores, etc.). For example (psuedo code) you could do something like this:
var pages = [];
var totalPages = GetTotalPages(); // request via ajax like you mentioned (function not shown)
var pagesLoaded = 0;
for(var i = 0; i < totalPages; i++)
{
GetData(pageIdx);
}
function GetData(pageIdx)
{
$.ajax({ ..., success: function(data){GetData_Callback(pageIdx,data);}});
}
function GetData_Callback(pageIdx, data)
{
pages[pageIdx] = data; // assign this specific page of data
pagesLoaded++;
if (pagesLoaded === totalPages)
{
// fully loaded; trigger event or call function to render, etc.
}
}

Do you just mean without the async: false?
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem=0&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=true",
dataType: "html",
success: function(data) {
console.log('test1'); // first response ok
data=convert(data);
$(data).find('Listing').each(function(){
$(this).find('total').each(function(){
totalList = $(this).text();
totalList = parseInt(totalList);
totalPages = totalList/10;
});
});
var startItem=0;
console.log(totalPages); // total page should be equal too "loop" logged
for (i = 0; i < totalPages; i++){
console.log('loop'); // enter the loop
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: function(data) {
console.log('test2'); // number of test 2 = nb of loop = totalPages
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
startItem = startItem + 10;
popResults();
},
});
}
},
});

The problem here is that you do not increment startItem until you receive a response. Your code is probably making multiple requests with startItem === 1 before the first response is even received, and so you will get some really weird behavior (probably will get duplicate responses, and you will only get the first few pages of data).
Try this instead. It still uses your loop but it increments startItem in the loop before the next request is made to insure that all pages of data are requested.
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem=0&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=true",
dataType: "html",
async: false,
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
$(this).find('total').each(function(){
totalList = $(this).text();
totalList = parseInt(totalList);
totalPages = totalList/10;
});
});
},
});
var startItem = 1;
for (i = 0; i < totalPages; i++){
$.ajax({
type: "POST",
url:server url here,
data:"creativeID=test&CompanyId=BHSR&StartItem="+startItem+"&streetlocation="+choiceTown+"&Location="+sectCode+"&PriceMin="+choiceMin+"&PriceMax="+choiceMax+"&ListingType="+checkRB+"&OpenHouse=false&NewDev=false&AuthenticationId=id&ReturnTotal=false",
dataType: "html",
success: function(data) {
data=convert(data);
$(data).find('Listing').each(function(){
results_xml.push($(this));
});
result_index=0;
result_image_counter=1;
popResults();
},
});
// increment start item BEFORE the next request, not in the response
startItem += 10; // now the next request will be for 11, 21, 31, 41, etc...
}
You may want to get familiar with your javascript debugger to see the behavior for yourself.

Related

Localstorage setting through ajax data randomly gets undefined

I am setting items to localstorage using the below code. The issue I am getting is whenever I refresh the page some of the items are not set when I inspect the localstorage it shows as undefined, this is absolutely random and sometimes all items are set parfectly. How can I ensure that all the items are set without any undefined?
const catdata = [];
var catArr = JSON.parse(localStorage.getItem('cat'));
for (let i = 0; i < catArr.length; i++) {
const catid = catArr[i]['id'];
const catname = catArr[i]['name'];
$('#topitems').prepend('<div ><a class="topitems" href="'+catArr[i]['id']+'">'+catArr[i]['name']+'</a></div>');
(function(i) {
$.ajax( { url : "sales/item_search_cat?cat="+catid
, data : "GET"
// , async: "false",
, dataType : "json"
, success : function(data) {
catdata.push(data);
localStorage.setItem(catid,JSON.stringify(catdata[i]));
}
});
})(i);
}
The execution of the ajax requests are not sequentially executed, and sometimes your code is accessing an index that doesn't exist.
For example, one response arrives with index 2, the code pushes into the empty array at index 0, and catdata[i] is trying to get the index 2.
I think you have to call the ajax request using await or you can use the data directly:
localStorage.setItem(catid, JSON.stringify(data));
Possible solution (I didn't test it):
The execution is async, if you need to wait for the executions, you have to follow an async/await approach.
function callAjax(catid) {
$.ajax({
url: "sales/item_search_cat?cat=" + catid,
data: "GET",
dataType: "json",
success: function(data) {
localStorage.setItem(catid, JSON.stringify(data));
success(data);
}
});
}
let catArr = JSON.parse(localStorage.getItem('cat'));
catArr.forEach(function({id: catid, name: catname}) {
$('#topitems').prepend('<div ><a class="topitems" href="' + catid + '">' + catname + '</a></div>');
callAjax(catid);
});

How to handle an ajax request that uses a callback function that has an ajax request itself?

My main issue is that buildItem(data) returns 'undefined'. I've gone through some links on stackoverflow about how to do callback functions but I think that it's getting a bit messy since I'm going through multiple functions here.
I have a list that I'm appending to in this ajax call.
$.ajax({
dataType: "json,
url: "/retrieveTeamView.do?managerEmail=${email}",
cache: false,
success: function (data, status) {
$('#nestable ol').append(buildItem(data));
});
I defined buildItem() as such:
function buildItem(item) {
getTotalCost(item, function(totalCost) {
html += "<span>" + totalCost + "</span>";
return html;
}
}
So all buildItem does is it takes the data and passes it into getTotalCost.
Here my getTotalCost() function should take the item, as it has an attribute "email" that I need for the url, and it should do a loop through the data retrieved from the ajax call to return a totalCost.
function getTotalCost(item, callback) {
$.ajax({
dataType: "json",
url: "/retrieveUserUsage.do?email=" + item.email,
success: function(data) {
var totalCost = 0;
for (var i = 0; i < data.length; i++) {
totalCost += parseFloat(data[i].cost);
}
callback(totalCost);
}
});
}
My understanding is that callback(totalCost) returns back to buildItem() and returns the total cost successfully. From what I understand, doing alert(html) gives me a correct output in the alert dialog, but it is not successfully appended in the ajax function. Am I doing something wrong?
Note: I deleted chunks of details to keep the idea but to still keep the code secretive, so sorry if some parts may not seem like they're used.
I am thinking that the issue is related to the fact that AJAX calls are asynchronous. Therefore, it may be trying to update the DOM before everything is returned.
Therefore you need to nest the calls.
you could try something like this:
$.ajax({
dataType: "json,
url: "/retrieveTeamView.do?managerEmail=${email}",
cache: false,
success: function (data, status) {
$.ajax({
dataType: "json",
url: "/retrieveUserUsage.do?email=" + data.email,
success: function(data) {
var totalCost = 0;
for (var i = 0; i < data.length; i++) {
totalCost += parseFloat(data[i].cost);
}
$('#nestable ol').append("<span>" + totalCost + "</span>");
}
});
}
});

Ajax saving array into global variables not working keep getting undefined

Trying to save an array retrieved from ajax into a global variable such that I may use it later on but keep getting undefined error
<script>
var items = [];
function add(value){
items.push(value);
}
$(document).ready( function() {
$.ajax({
type: 'POST',
url: 'xxxx.php',
dataType: 'json',
cache: false,
success: function(result) {
for(i=0; i < result.length; i++){
add(result[i]);
}
},
});
});
document.write(items[1])
</script>
This is an asynchronous AJAX call. The call to add will be done at a later time than the execution of document.write(items[1]);
So this is the right way to do it:
<script>
var items = [];
function add(value){
items.push(value);
}
$(document).ready( function() {
$.ajax({
type: 'POST',
url: 'xxxx.php',
dataType: 'json',
cache: false,
success: function(result) {
for(i=0; i < result.length; i++){
add(result[i]);
}
document.write(items[1])
},
});
});
</script>
This way the function that uses the result, will be executed when the result function is executed.
Think it this way: you said: Here is this lemon basket. Then you asked someone to go somewhere and get the lemons and before he returned you tried to count the lemons. Got it ?
You can use ajaxstop to call the method after the ajax request has completed. Place the following function in document ready:
$(document).ajaxStop(function () {
document.write(items[1]);
});
The above solution works but this is another option I used when waiting for multiple ajax functions to complete. This will call every time you complete an ajax request but there is a way to limit to one call if need be.

AJAX - Wait for data to append before looping next

I am creating a drop down list in JavaScript, I am loading the data via Ajax and JSON, at the moment my code loops through a set of departments and runs into the ajax call in each iteration.
My problem is that my data seems to be appending in a random order, its likely that its loading in the order of whichever loads quickest.
I want to be able to loop through my Ajax call and append the data in the order that I declare (for each department).
is this something that can be done?
Here is my code:
//-- Ajax --
var departments = ['Accounts', 'Commercial', 'Installation', 'Production', 'Sales'];
var i;
for (i = 0; i < departments.length; i++) {
$.ajax({
type: "POST",
url: "Default.aspx/EmployeesDropDown",
data: '{X: "' + departments[i] + '"}',
contentType: "application/json; charset=utf-8",
dataType: "text json",
async: true,
success: createdropdown,
failure: function () {
alert("FAIL!");
}
});
}
//-- Creates dropdown --
function createdropdown(data) {
...appends all the data to my drop down list...
}
Any help or advice is appreciated, thank you in advance.
EDIT: This question is different from the related ones because I need to be able to loop through the string array, rather than just iterating based on numbers.
If you want to load the departments in the order they appear in the departments array you could load them one by one, waiting for each ajax request to finish until you start the next request.
Here is an example:
var departments = ['Accounts', 'Commercial', 'Installation', 'Production', 'Sales'];
var i = 0;
function reqDep(department) {
/*
Since i can't use ajax here let's use a promise.
*/
var p = new Promise(function(res, rej) {
setTimeout(function() {
res(department)
}, 1000)
})
return p;
// This is what you would actually do.
/*
var data = '{X: "' + department + '"}'
return $.ajax({
type: "POST",
url: "Default.aspx/EmployeesDropDown",
data: data,
contentType: "application/json; charset=utf-8",
dataType: "text json",
});
*/
}
function initDepartments(index) {
reqDep(departments[index])
// Here you would use `.done(function(data...`
// I am using `.then(function(data...`
// because of the promise.
.then(function(data) {
console.log(data)
if(i < departments.length) {
initDepartments(i)
}
})
i++;
};
initDepartments(i)

Ajax Pagination with jQuery

I need to retrieve data from an api that has a limit on the number of results being returned.
Using the total number of results found, I'm able to make additional additional ajax calls until all data is retrieved. However, I need sort all the data alphabetically before displaying it on the page.
How can I access the data within the array after ALL ajax calls have been successfully made?
Here is what I have so far:
var mydata = [];
function loadPosts() {
var api = 'http://someurl.com';
$.ajax({
url: api,
dataType: 'jsonp',
data: {
rows: 1,
},
success: function(data) {
totalNumberOfRecords = data.numFound;
for (var start = 0; start < totalNumberOfRecords; start += rows) {
$.ajax({
url: api,
dataType: 'jsonp',
data: {
'rows': rows,
'start': start,
},
success: function(data) {
$.each(data.namesList, function(index, item) {
mydata.push(item.firstName);
});
},
});
}
},
});
}
console.log(mydata);
This is an instant-noodle solution.
var numOfAjax = 0;
var numOfResolvedAjax = 0;
for (var start = 0; start < totalNumberOfRecords; start += rows) {
numOfAjax++;
$.ajax({
url: api,
dataType: 'jsonp',
data: {
'rows': rows,
'start': start,
},
success: function(data) {
$.each(data.namesList, function(index, item) {
mydata.push(item.firstName);
});
numOfResolvedAjax++;
// Check if all ajax request has been responded to sort the result.
if (numOfResolvedAjax === numOfAjax) {
sortMyData(myData);
}
},
});
}
For a perfect solution, please check in async library with a lot of functions which can help you. E.g: async.parallel may be what you need.
Honestly, if it were me with this API limit issue, I'd use the server to talk to the API ever-so-often to capture all the data into my own system - beit MySQL or Redis or whatever. Then I would use my front-end JS however I wanted without limmits

Categories

Resources