How can I a group jQuery AJAX queries? - javascript

I have some code that looks like this (I left out the parts that are unimportant for the question):
$.ajax({
type: "POST",
url: "prepXML.php",
data: "method=getartists&user=" + userName + "&amount=" + amount,
dataType: "xml",
success: function(xml) {
$("artist", xml).each(function(){
// calculate and output some stuff and then call getTracks()
getTracks(artistName, artistNamePOST, artistPlaycount, divId, artistId);
});
}
});
function getTracks(artistName, artistNamePost, artistPlaycount, divId, artistId){
$.ajax({
type: "POST",
url: "prepXML.php",
data: "method=gettracks&user=" + userName + "&artist=" + artistNamePOST + "&playcount=" + artistPlaycount,
dataType: "xml",
success: function(xml){
// calculate and output some stuff
});
}
});
When this is run, it calls getTracks() fifty times and makes quite some (server) CPU load in very short time until it all gets done. What I would like to do is to group AJAX getTrack() queries by for example 5 at a time, wait until those five are done, then call the next five, wait until the next five are done, call the next five etc. The purpose of this would be to make fewer queries at basically the same time (less and more evenly spread CPU load).
I am unsure how or even if this can be done since it kind of partially beats the point of AJAX, but I would still like to make this work if possible. Could someone please point me into the right direction? Thank you.
To better understand what I need this for and what the app does, here is a link to the app. it can be tried out with any lastfm nick (if you don't have one, you can use mine - "pootzko"). I hope I am allowed to put the link in the post(?), if not, feel free to remove it..

I'd consider only retrieving track info for artists that a user has clicked on. Or possibly retrieving all of the data via a single request (perhaps then after it is retrieved process it in batches via setTimeout()).
But something along the lines of the following might work to do only five requests at a time:
$.ajax({
type: "POST",
url: "prepXML.php",
data: "method=getartists&user=" + userName + "&amount=" + amount,
dataType: "xml",
success: function(xml) {
var artists = $("artist", xml),
i = 0,
c = 0;
function getTracksComplete() {
if (--c === 0)
nextBatch();
}
function nextBatch() {
for(; c < 5 && i < artists.length; i++, c++) {
// artists[i] is the current artist record
// calculate and output some stuff and then call getTracks()
getTracks(artistName, artistNamePOST, artistPlaycount, divId, artistId,
getTracksComplete);
}
}
// Optional - if you need to calculate statistics on all the artists
// then do that here before starting the batches of getTracks calls
artists.each(function() { /* do something */ });
// Kick of the first batch of 5
nextBatch();
}
});
function getTracks(artistName, artistNamePost, artistPlaycount, divId, artistId,
callback){
$.ajax({
type: "POST",
url: "prepXML.php",
data: "method=gettracks&user=" + userName + "&artist=" + artistNamePOST + "&playcount=" + artistPlaycount,
dataType: "xml",
success: function(xml){
// calculate and output some stuff
},
complete : function() {
if (callback) callback();
});
}
});
The above was just off the top of my head (so I didn't have time to build it to scale or to paint it), but the idea is that instead of using .each() to loop through all of the artists at once we will cache the jQuery object and do a few at a time. Calling the function nextBatch() (which is local to the success handler and thus has access to the local variables) will run a loop that calls getTracks() only 5 times, but starting processing from where we left off the previous time. Meanwhile getTracks() has been updated slightly to accept a callback function so when its ajax call completes (and note we do this on complete rather than success in case of errors) it can let the main process know that it has finished. Within the callback we keep track of how many have completed and when they all have call nextBatch() again.

Either include all the track information in the data that is returned from getartists OR only call getTracks when someone wants to see the tracks for a specific Artist.
e.g.
Display all the Artists, and have a "View Tracks" options. Only once this is clicked then you get the tracks.
To me this is the best option because if you are loading up ALL the tracks for ALL the artists, then there is a lot of data that that probably isnt needed. No one is going to want to look through all the artists and all the artists tracks (unless the specifically want to).

My solution is to process all the artist data into the array and then start executing .ajax() requests in the batches of 5. When those 5 requests are done, next batch is executed, etc.
HERE is a working demonstration.
// just success changed
$.ajax({
type: "POST",
url: "prepXML.php",
data: "method=getartists&user=" + userName + "&amount=" + amount,
dataType: "xml",
success: function(xml) {
var data = [];
// prepare the data
$("artist", xml).each(function(){
data.push({ name: artistName /** rest of data */ } );
});
// start processing the data
processData(data, 0);
}
});
// process the data by sending requests starting from index
function process(data, index) {
var xhrs = [];
for (var i = index; i < index + 5 && i < data.length; i++) {
(function(data) {
xhrs.push($.ajax({
type: "POST",
url: "prepXML.php",
data: "method=gettracks&user=" + data.name /** rest of the data */
dataType: "xml",
success: function(xml) {
// calculate and output some stuff
}
}));
})(data[i]);
}
// when current xhrs are finished, start next batch
$.when.apply(this, xhrs).then(function() {
index += 5;
if (index < data.length) {
process(data, index);
}
});
}

It's difficult to clearly understand the logic of you application, but I think a better way should be collecting previously all the data you need to send (50 entries in a JSON object) and then call getTracks function only once, passing only one JSON object.

Related

How can I check if there are changes in my database

What I want is a technique to refresh my div if there are changes in my database. Here is the point,
What i want: How can i condition to know if the first value from my database is lesser than the upcomming value.
In my situation, i put my ajax function to be run every 5secs here is it:
lastcountQueue is declared as global in javascript
function check_getqueue() {
$.ajax({
url: siteurl+"sec_myclinic/checkingUpdates/"+clinicID+"/"+userID,
type: "POST",
dataType: "JSON",
success: function(data) {
lastcountQueue = data[0]['count'];
}
});
}
Q:where would i put the condition something if lastcountQueue < data[0]['count]; condition means something if the data is lesser than lastcountQueue it means there was a change in my database portion.
Another Clear Situation for my question:
I want to make a function like these: the ajax will run every 5 seconds where it query a value to count my no. of queues in database. If my first query is giving me 5 value, and the second is giving me again another 5, then there must be nothing change happens, then if my third value gives me 4, where it is not equal to the last query, then i would do something
Probably something like this:
function check_getqueue() {
$.ajax({
url: siteurl+"sec_myclinic/checkingUpdates/"+clinicID+"/"+userID,
type: "POST",
dataType: "JSON",
success: function(data) {
var tmpCountQ = data[0]['count'];
if (tmpCountQ < lastcountQueue) {
// Process the change
}
lastcountQueue = tmpCountQ;
}
});
}
Here is the updated answer:
function check_getqueue() {
$.ajax({
url: siteurl + "sec_myclinic/checkingUpdates/" + clinicID + "/" + userID,
type: "POST",
dataType: "JSON",
success: function(data) {
if (data[0]['count'] != lastcountQueue) {
//Your logic here
lastcountQueue = data[0]['count'];
}
}
});
}

JQuery Ajax limiting deep recursion "too much recursion"

The idea is that I send lots of synchronous requests to an API to create a JSON that I will need later to do some clusterizations. This API gives me information about articles, review etc. from a site (scopus.com). First I send a request based on a query from which I get a JSON which contains information about some articles. These articles are cited by other articles. I have to get information about these ones too so I need recursion. The problem is that I get an error because of "too much recursion". It seems that the error appears when the recursion is over and the program has to go back to the "root"/the first call. So the program will look like a very deep tree.
Pure Javascript does have this limitation too? What can I do?
Also I have to do SYNCHRONOUS requests otherwise the JSON I will get will be a mess.
EDIT:
I tested the script on queries that need a small recursion such as a tree with 4-5 levels.
var result = '{"entry":[ ';
function query(){
var results = getNumberResults();
if(results>0)
{
var pages = Math.ceil(results/25);
var i;
for(i=0; i<pages; i++){
$.when($.ajax({
url: url,
type: "GET",
async: false,
headers: {'Accept':'application/json'},
success: function(data){
$.each( data['search-results']['entry'], function( i, item ) {
get info from json and save it in my variable
if(data['search-results']['entry'][i]['citedby-count'] > 0)
getCitedBy(data['search-results']['entry'][i]['eid']);
else{
result += '"children-id":[]},';
}
});
}
}));
}
}
result = result.slice(0,-1);
result += "]}";
}
function getCitedBy(eid){
var results = getCitedByNumberResults(eid);
if(results>0)
{
var pages = Math.ceil(results/25);
var i;
for(i=0; i<pages; i++){
$.when($.ajax({
url: url,
type: "GET",
async: false,
headers: {'Accept':'application/json'},
success: function(data){
$.each( data['search-results']['entry'], function( i, item ) {
get info from json and save it in my variable
if(data['search-results']['entry'][i]['citedby-count'] > 0)
getCitedBy(data['search-results']['entry'][i]['eid']);
else{
result += '"children-id":[]},';
}
});
}
}));
}
}
}
function getNumberResults(){
var innerResult;
$.ajax({
url: url,
type: "GET",
async: false,
headers: {'Accept':'application/json'},
success: function(output){
innerResult = output['search-results']['opensearch:totalResults'];
},
error: function (xhr, ajaxOptions, thrownError) {
innerResult = 0;
}
});
return innerResult;
}
function getCitedByNumberResults(eid){
var innerResult;
$.ajax({
url: url,
type: "GET",
async: false,
headers: {'Accept':'application/json'},
success: function(output){
innerResult = output['search-results']['opensearch:totalResults'];
},
error: function (xhr, ajaxOptions, thrownError) {
innerResult = 0;
}
});
return innerResult;
}
The problem was as trincot mentioned and I also thought so was that 2 or more articles are referencing each other making an infinite cycle. I fixed it by searching my string variable for the unique identifier. So if in my variable already exists and article with that identifier I will skip recursion for the current article.
So I tested my script again for relative short queries (few hundreds of articles returned) because there are queries with huge outputs (millions of articles). When I will search for big queries I could come upon string size limitations(browser specific) or even “too much recursion”. If so I will let you know.
Advice: if “too much recursion” error occurs in your ajax request search first for an infinite cycle because this is the most probable cause.

Updating a list with ajax response data in a specific order

I have an ajax request that gets called several times based on the number of request objects in an array. The order in which these objects are in inside of the array is important, and needs to be reflected in a dynamically generated list in that same order. When the server sends back each response I update a <ul> as shown below.
$.ajax({
type: 'POST',
url: baseServiceURL + 'report/',
processData: false,
dataType: 'json',
contentType: 'application/json',
data: payload,
crossDomain: true,
})
.done(function (response) {
updateUI(response);
})
.fail(function (jqXHR, textStatus) {
// handle failure
});
var updateUI = function (response) {
// Update the drop-down list
$('#dropdown').append('<li><a class="dd-option" data-value="' + response.ReportName + '" data-path="' + response.ReturnURL + '" href="#">' + response.ReportName + '</a></li>');
// do more stuf...
};
How can I dynamically build the list in such a way to where the response display in the proper order? One thing I have done is add a order param to the request who's value is the index of the request object in the array. My thought is my service can send that value back in the response so the javascript can act on it.
EDIT: The question here is asking basically the same thing except rather than using a getJSON command and appending divs I'm using a post and appending <li> elements.
There are two possible strategies here.
Update your UI immediately upon receiving response and then re-render if a new value is received
Wait until all ajax replies have finished and then render your UI
For (1) you should just keep a running total of all items
var $dropdown = $('#dropdown');
var renderDropdown = function(reports) {
//use lodash or underscore.js here cause implementing this manually is annoying
var sortedSelections = _.sortBy(reports, 'order');
var htmlPerItem = sortedSelections.map(function(item) {
return '<li><a ..... </li>';
});
$dropdown.html(htmlPerItem.join(''));
}
var reportSelections = [];
payloads.map(function(payload) {
$.ajax({ ... })
.then(function(response) {
reportSelections.push(response);
renderDropdown(reportSelections);
})
})
for (2) you can use jquery $.when
var gettingResults = payloads.map(function(payload) {
return $.ajax({ .... });
});
$.when(gettingResults).then(function() {
//the first arg will be response1, the second response2, etc
var reportSelections = _.sortBy(arguments, 'order');
renderDropdown(reportSelections);
});
Note in (1) you render once per item but get an updating view as items come in. In (2) you render only once but have to wait until all loading is complete.
Of course a variation of (1) is that you don't re-render anything, but merely insert items into the correct location as they are loaded. That's going to be more difficult to debug and more code so I leave it as an exercise for the reader (use jquery's $.fn.data to store the original item with the element).

Javascript autocomplete with ajax call order

I have simple autocomplete input field with Javascript like this:
$('#search').on('keyup', function () {
var query = $(this).val();
$.ajax({
type: "GET",
url: "/search",
data: { query: query }
}).done(function (results) {
showSearchResults(results);
});
});
Sometimes first call takes more time then second or third and results are overridden.
How can I make sure that results only from the latest successful call are displayed?
I mean if I got response from call #3 - I no longer care about calls #1 and #2 and don't want them to override results of call #3.
Ajax function is in default asynchronous it means that many of functions can run on same time. If You wrote 3 letters it will run 3 times, after 3 keyups. If you want to run function in sequence just add setting async: false.
$.ajax({
type: "GET",
url: "/search",
async: false,
data: { query: query }
}).done(function (results) {
showSearchResults(results);
});
But i think You should add some delay, so function will not run immediately after every keyup, just after last one.
I suggest that you bind an incremental id to each ajax request that you send. When you get a response, just check that it carries last given id.
let lastXHRid=0; // tracker of last sent ajax request
$('#search').on('keyup', function () {
let reqXHR = $.ajax({ // create a variable out of $.ajax returned value
type: "GET",
url: "/search",
data: { query: $(this).val() }
});
lastXHRid++; // increment the XHR counter
reqXHR.id = lastXHRid; // attach id to the request
reqXHR.done(function(results, status, respXHR) {
if ( respXHR.id == lastXHRid ){ // compare id of received and last sent requests
showSearchResults(results);
}
});
});
(Edit: I initially suggested tracking the unix timestamp of last sent request, but as #seth-battis suggested in the comments, a sequence number is far enough. As a bonus, I also debugged my sample snippet!)

In Javascript, process list synchronously

I have a given collection of items to be processed; each item must wait for the completion of the previous one.
By collection of items I mean an array of integer values.
By "processing" I mean make a POST to a HTTP server, passing the integer value of each array element.
I've found something that looks like waht I'm looking for: doSynchronousLoop.js but I wonder if there are alternatives.
If your site may pause rendering while doing the requests, here's a solution with jQuery:
// process 5 items
for (var i = 0; i< 5; i++) {
// ajax request done with jquery
$.ajax({
async: false, /* this makes it execute synchronously */
url: "the url to handle item #i",
type: "POST",
success: function(msg) {
// process data for item #i
}
})
}
Edit: you can solve it asynchronously, too:
items = [put your items here]
current_item = 0
function processItem() {
if (current_item == items.length) {
// list processing finished
return;
}
$.ajax({
async: true,
url: "the url to handle item #current_item",
type: "POST",
success: function(msg) {
// process data for item #current_item
processItem();
current_item++;
}
})
}
Don't miss to put the variables in a scope, I just left them in global scope to make the example easier to understand.
See also the docs: jQuery.ajax

Categories