Unknown amount of Ajax Request inside loop - javascript

So here is my problem (and I have tried several suggestions found here at stackOverflow):
Scenario:
I am using the Gitlab API and I want to list all the "issues" of the bug tracker present on the system for a given project.
Problem:
This is all fine and good, however there is a paging system to do this since the ajax requesst is limited to 100 entries per response.
So I have to do something like this:
$.ajax({
url: "https://mygitlabURL/api/v3/projects/97/issues",
type: "GET",
data: {
private_token: "mytoken",
per_page: 100,
page: 1
}
This will give me back 100 entries. What I need to do is add these entries to a list, and check: "was there fewer than 100 entries in the response?" if so I can stop doing requests.
I need however to get the complete list before I can move on with my code, so I tried using $.when() function and do my requests in a function of its own. In this function I have tried using:
closures like in this answer
recursion like suggested in another answer (don't have the link)
while loop since, oh well, why not
The problem with all the solutions is that, beeing asynchronous, I end up receiving a response and my $.when() function executes before I have any response from the server.
Is there a way to do this?
Here is the latest code (with recursion) I have tried:
$(function () {
$("button").on("click", function () {
$.when(func1(), func2()).then(finishedFunc);
});
});
var func1 = function (pageNr) {
pageNr = pageNr || 1;
megaList = [];
// Get server values
$.ajax({
url: "https://mygitlabURL/api/v3/projects/97/issues",
type: "GET",
data: {
private_token: "myToken",
per_page: 100,
page: pageNr
},
success: function (issuesList) {
console.log("Page number: " + pageNr);
megaList = [pageNr];
if (issuesList.length < 100) {
return megaList;
}
pageNr = pageNr +1 ;
var received = func1(pageNr);
megaList = $.merge(megaList, received);
return megaList;
}
});
}
var func2 = function () {
return 20;
}
var finishedFunc = function (resp1, resp2) {
console.log("Responses were resp1: " + resp1 + " and resp2: " + resp2);
}
And I always get something like:
"Responses were resp1: undefined and resp2: 20"
And I am expecting something like:
"Responses were resp1: [1, 2, 3, 4, 5, ..., 27] and resp2: 20"
As stated before, I can't find any solutions that resolve my problem here in the forums, but if I might have overlooked something, please point me in the right way.

While reading the documentation.I came across this.
Pagination
When listing resources you can pass the following parameters:
page (default: 1) - page number
per_page (default: 20, max: 100) - number of items to list per page
Link headers are send back with each response. These have rel prev/next/first/last and contain the relevant URL. Please use these instead of generating your own URLs.
It automatically says that the Response that came back will contain rel prev/next/first/last. So you can easily check that link headers contain next rel or not and If it contain then directly call that url for more issues and If not that means it does not contain more issues.

Once you start to think in async terms the solution become pretty simple:
var megaList = [];
function loadList(page) {
page = Math.max(1, page);
$.ajax({
url: "https://mygitlabURL/api/v3/projects/97/issues",
type: "GET",
data: {
private_token: "myToken",
per_page: 100,
page: page
},
success: function (issuesList) {
console.log("Page number: " + page);
megaList = megaList.concat(issuesList);
if (issuesList.length >= 100) loadList(page+1);
}
});
}
loadList();

Related

Deferred jquery ajax

I've looked through all the issues on this, I think.
I have a function which uses jquery to update a record via ajax. I need to change it to do them and wait for each one to finish.
I've been reading about $.Deferred and have tried to implement, but the code still doesn't wait. I understand that ajax is asynchronous and I don't want to change that as such, but I do want it to wait, as the code outputs messages and I need them in order.
My code is as such:
//do the export:
function doExport(id) {
var exportComplete = $.Deferred();
var exportPromise = $.ajax({
type: "POST",
url: "import.php",
dataType: "json",
data: { id: id }
});
exportPromise.done(function(msg) {
//msg contains the text from the import
exportComplete.resolve(msg);
});
return exportComplete.promise();
}
//export all ticked items (from a list)
function importInvoices() {
var checked = new Array;
$('#apiCall').html("");
//put all checked items into an array (id corresponds to a PK in the db)
$("input[name=selectedRow]:checked").each(function(num,row) {
checked.push($(row).data('id'));
});
//loop through each checked item
$(checked).map(function(num, id) {
console.log("starting " + id) ;
var prom = doExport(id).then(function(result) {
console.log("done " + id);
$('#apiCall').html($("#apiCall").html() + result.messages);
});
});
$("#pleaseWaitContainer").hide();
}
It must be so close, but I guess I've got something missing or in the wrong place. The text from the resulting export does appear where it should, but if (for example) I select 3 invoices, I get the console text of "starting xxx" 3 times, and then the "done xxx" four times.
Like this:
starting 243
starting 663
starting 823
done 243
done 663
done 823
Whereas what I want is
starting 243
done 243
starting 663
done 663
starting 823
done 823
Any advise would be lovingly appreciated... I'm tearing my limited hair out.
Chris
Call in Success
const checked = $("input[name=selectedRow]:checked").map(function() {
return $(this).data('id'))
}).get()
const load = function() {
$.ajax({
url: `/someprocess.php?id=${checked.shift()}&otherparm=bla`,
success: function() {
if (checked.length > 0) load()
}
})
}
if (checked.length > 0) load(); // start

How to increment skip count until results is found in API?

I have the following api which returns thousands of result: http://xxx/xxx/CarRoutes. The API creator however limits only 50 results to be return at once. Hence, to see get another 50 more results to be returned, "?$skip=50" needs to be used. Also, the api url does not allow to add in any parameters behind.
Now I would like to search for CarRoutes id = 123. How can I auto increment the $skip count until results is found?
Appreciate if it can be done Javascript language.
Current idea I have, which is not efficient.
function getInfo() {
$.ajax({
url: "http://xxx/xxx/CarRoutes?$skip="+skip,
success: function(result) {
var obj = JSON.stringify(result);
var routetimeobj = JSON.parse(obj);
var data = routetimeobj['value'];
var data_filter = data.filter(element => element.CarRoute =="123");
if(data_filter.length==0){
skip+=50;
getInfo();
return;
}
});
};
If you want to follow the pattern you are using in your code, you can add a parameter to your function
function getInfo(skip)
and when you are calling again, call it like this
getInfo(skip + 50);

How to loop through GET/POST calls sequentially (waiting for previous) return?

I'm writing a Tampermonkey script for a web page and trying to extract data from other pages.
I'm trying to make a function that has a loop inside that goes thru a list, llcList, and retrieves data from ajax method GET, but would like to wait for to finish one request before going to second one.
Bonus would be if I could make it wait some extra time.
What should happen:
send request for a llcList[0]
get return data, process it
wait some time
send new request for a llcList[1]
Is this possible? I tried few methods, every time loop send all requests not a second apart. :
function F_Company_LLC(){
for (i = 0; i < llcList.length;i++) {
if(llcList[i][2]=="lab"){
//run function 0
//break;
}
else if(llcList[i][2]=="shop"){
//run function 1
//break;
}
else{
F_GET_CompData(llcList, llcList[i][1],i,function(result){
console.log(result);
});
}
}}
function F_GET_CompData(F_GET_CompData_list, CompID, F_GET_CompData_row, callback){
$.ajax({
method : "GET",
url: base_link+"/company/edit_company/"+CompID,
beforeSend: function(){runningRequest++;},
success: function(data){
//data processing
runningRequest--;
},
error: function() {console.log("Get_ComData");}
});
callback(runningRequest);}
This is a common scenario. Note that it's often unnecessary to process the calls sequentially though. It's usually adequate to just send context with the ajax calls and piece everything together as it comes in semi randomly, as shown in this answer.
One way to force sequential behavior is to chain calls via the complete function. Here is fully functional code that demonstrates the process. To use, paste it into your browser console while on a Stack Overflow page. :
var listO_pages = ["q/48/", "q/27/", "q/34/", "q/69/", "badpage"];
var numPages = listO_pages.length;
getPageN (0); //-- Kick off chained fetches
function getPageN (K) {
if (K >= 0 && K < numPages) {
let targPage = listO_pages[K];
$.ajax ( {
url: "https://stackoverflow.com/" + targPage,
context: {arryIdx: K}, // Object Helps handle K==0, and other things
success: processPage,
complete: finishUpRequest,
error: logError
} );
}
}
function processPage (sData, sStatus, jqXHR) {
//-- Use DOMParser so that images and scripts don't get loaded (like jQuery methods would).
var parser = new DOMParser ();
var doc = parser.parseFromString (sData, "text/html");
var payloadTable = doc.querySelector ("title");
var pageTitle = "Not found!";
if (payloadTable) {
pageTitle = payloadTable.textContent.trim ();
}
var [tIdx, tPage] = getIdxAndPage (this); // Set by `context` property
console.log (`Processed index ${tIdx} (${tPage}). Its title was: "${pageTitle}"`);
}
function finishUpRequest (jqXHR, txtStatus) {
var nextIdx = this.arryIdx + 1;
if (nextIdx < numPages) {
var tPage = listO_pages[nextIdx];
//-- The setTimeout is seldom needed, but added here per OP's request.
setTimeout ( function () {
console.log (`Fetching index ${nextIdx} (${tPage})...`);
getPageN (nextIdx);
}, 222);
}
}
function logError (jqXHR, txtStatus, txtError) {
var [tIdx, tPage] = getIdxAndPage (this); // Set by `context` property
console.error (`Oopsie at index ${tIdx} (${tPage})!`, txtStatus, txtError, jqXHR);
}
function getIdxAndPage (contextThis) {
return [contextThis.arryIdx, listO_pages[contextThis.arryIdx] ];
}
This typically outputs:
Processed index 0 (q/48/). Its title was: "Multiple submit buttons in an HTML form - Stack Overflow"
Fetching index 1 (q/27/)...
Processed index 1 (q/27/). Its title was: "datetime - Calculate relative time in C# - Stack Overflow"
Fetching index 2 (q/34/)...
Processed index 2 (q/34/). Its title was: "flex - Unloading a ByteArray in Actionscript 3 - Stack Overflow"
Fetching index 3 (q/69/)...
Processed index 3 (q/69/). Its title was: ".net - How do I calculate someone's age in C#? - Stack Overflow"
Fetching index 4 (badpage)...
GET https://stackoverflow.com/badpage?_=1512087299126 404 ()
Oopsie at index 4 (badpage)! error Object {...
-- depending on your Stack Overflow reputation.
Important: Do not attempt to use async: false techniques. These will just: lock up your browser, occasionally crash your computer, and make debug and partial results much harder.

Javascript scope/hoisting OR promises/deferreds?

I am trying to make an external AJAX call to an API within a Jquery each loop.
Here is the code I have so far.
getStylesInfo(tmpMake, tmpModel, tmpModelYear, tmpSubmodel).done(function(data){
var holder = [];
$.each(styles, function(index, value) {
var tempValue = value;
var temp = getNavigationInfo(value.id);
$.when(temp).done(function(){
if(arguments[0].equipmentCount == 1){
holder.push(tempValue);
console.log(holder);
}
});
});
});
console.log(holder);
function getStylesInfo(make, model, year, submodel){
return $.ajax({
type: "GET",
url: apiUrlBase + make + '/' + model + '/' + year + '/' + 'styles? fmt=json&' + 'submodel=' + submodel + '&api_key=' + edmundsApiKey + '&view=full',
dataType: "jsonp"
});
function getNavigationInfo(styleId){
return $.ajax({
type: "GET",
url: apiUrlBase + 'styles/' + styleId + '/equipment?availability=standard&name=NAVIGATION_SYSTEM&fmt=json&api_key=' + edmundsApiKey,
dataType: "jsonp"
});
The getStylesInfo() returns something similar to this. An array of objects with info about a car model.
var sampleReturnedData = [{'drivenWheels': 'front wheel drive', 'id': 234321}, {'drivenWheels': 'front wheel drive', 'id': 994301}, {'drivenWheels': 'rear wheel drive', 'id': 032021}, {'drivenWheels': 'all wheel drive', 'id': 184555}];
I am trying to loop through the sampleReturnedData and use each id as a parameter in a different AJAX call with the getNavigationInfo() function.
I want to loop through the results and make a check. If it is true then I want to push the entire object to the holder array.
The problem is the console.log(holder) outside the function returns an empty array. The console.log(holder) within the if statement works properly.
I am not sure if this is a scope/hoisting issue or a problem with the way I am using deferreds?
I have read this question and many like it. They suggest to use either
async:false
Or to rewrite the code better. I have tried and used console debugger numerous times. I don't want to set it to false. I'm just unsure what exactly is going on.
I've also read up on hoisting via this article.
I believe it has to do with deferreds but I don't have enough JS knowledge to figure it out.
Thanks!
I am not sure if this is a scope/hoisting issue or a problem with the way I am using deferreds?
In fact, it is both:
holder is declared only within the callback function (as a local variable), so it's undefined outside the function.
And the console.log is executed before the asynchronous callback function does fill the array with values, so even if holder was in scope it would still have been empty. See also Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
So what you should do is indeed to rewrite your code to use promises properly :-)
getStylesInfo(tmpMake, tmpModel, tmpModelYear, tmpSubmodel).then(function(data) {
var holder = [];
var promises = $.map(data.styles, function(value, index) {
return getNavigationInfo(value.id).then(function(v){
if (v.equipmentCount == 1)
holder.push(value);
});
});
return $.when.apply($, promises).then(function() {
return holder;
}); // a promise for the `holder` array when all navigation requests are done
}).then(function(holder) {
console.log(holder); // use the array here, in an async callback
});

Interrupting an looping function

I trigger the following function when a tooltip is clicked. It is an ajax poll.
There can be many tooltips on the page, and more than one can need access to the data retrieved from the server.
What I want to achieve is to have this poll running as one instance - so if the user clicks a different tooltip the polling stops, rather than being duplicated.
Would be grateful if you could help.
Thanks
function doConversationsAjaxLongPoll(tablename){
clientSubmit = new Object;
// HERE WE'RE GOING TO GET A LIST OF THE ROWIDS THAT WE NEED TO POLL FOR, MAKE AN OBJECT OUT OF THEM. DO THIS BY LOOKING AT WHICH //TOOLIPS HAVE CLASS OPEN
var tooltips = [];
$('.tooltipOpen').each(function(index){
tooltips.push($(this).data('idrow'))
})
console.log("tooltips length: " + tooltips.length)
if(tooltips.length==0){
// console.log("tooltip length is 0 so we're returning false")
return false
}
clientSubmit.OpenConversations = tooltips
clientSubmit.tablename = tablename
clientSubmit.CurrentData = $('body').data('conversations')
console.log(clientSubmit)
$.ajax({
type: 'POST',
url: '/conversations.php?loadNew=1',
data: clientSubmit,
timeout: 25000,
success: function(data){
console.log('success')
data=JSON.parse(data)
console.log(data)
$('body').data('conversations', data)
},
complete: function(status, jqXHR){
if(tooltips.length==0){
// console.log("tooltip length is 0 so we're returning false")
return false
}
else
{
doConversationsAjaxLongPoll(tablename);
}
}
});
updateConversations()
}
I don't doubt that there are faaaar better ways of doing this but I have worked around the problem by having a random number generated by the click function, stored in $('body').data('random') which is then passed to the poll function. When the poll function loops it checks if the random number it was passed matches the one in data-random and returns false if it doesn't.

Categories

Resources