1

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:

  1. send request for a llcList[0]
  2. get return data, process it
  3. wait some time
  4. 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);}
Brock Adams
  • 82,642
  • 19
  • 207
  • 268
  • In general, avoid ajax calls inside loops. Research alternative solutions where you might make a single ajax call with enough information to update several records, get all records matching multiple companyIds or what ever it is you want to do, etc.. However, I guess a while-loop with complete callbacks or promises to determine when to continue might work? Though what if the call doesn't return and eventually times out, do you continue or not? - I rather would check for a solution which returns multiple records at ones with a single call. – Nope Nov 30 '17 at 17:14
  • I updated code for a point 3. now function LLC_Comp_Timer execute, increment variable i by one and wait for 1second before executing again – StillLearning Nov 30 '17 at 22:54

2 Answers2

1

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.

Brock Adams
  • 82,642
  • 19
  • 207
  • 268
-1

Simulate for-loop with async ajax requests. On ajax's complete callback go to next item in the list:

function F_Company_LLC(llcList) {
    var i= 0;

    function getNext() {

           if(llcList[i][2]=="lab"){
                //run function 0

                    ++i;

                    getNext();
            }
            else if(llcList[i][2]=="shop"){
                //run function 1

                    ++i;

                    getNext();
            }
            else{

             $.ajax({

              url: base_link+"/company/edit_company/"+llcList[i][1], //CompID
              method: 'GET',
              async: true,
              success: function(data) {
                if (data.status == "success" && i <= llcList.length) {
                     //data processing
                 }
               },
               error: function(xhr) { 
                alert("Error while processing CompID: " + llcList[i][1]);

               },
              complete: function() {
             //complete executes after either 
             //the success or error callback were executed.
                     ++i;
                    getNext();//go to next item in the list
              },
             });      
            }  
    }

    getNext();
}


F_Company_LLC(llcList);
katiforis
  • 703
  • 4
  • 15
  • That's fragile and unreliable. What if the network or queried database is experiencing a temporary slowdown and the timeout is not enough? It's a guessing game. Using something more concrete like promises or even better making a single call only outside the loop would be much more reliable. Code looks like the loop can build a list of identifiers that could have their company details returned in a single query. – Nope Nov 30 '17 at 17:20
  • The setTimeout is the extra time @user9034751 ask for. **It's not the wating time for the request**. If the network or queried database is experiencing a temporary slowdown, error: function() executed automatically by $.ajax and you can handle it (pop up "no internet" message to user). – katiforis Nov 30 '17 at 17:29
  • You have right that a single request is better but @user9034751 ask for multiple request implementation. – katiforis Nov 30 '17 at 17:35
  • I cant pull data in single request for all CompID in llcList, so i need to send request for each ID to get data and to process it. – StillLearning Nov 30 '17 at 19:59
  • In some cases that implementation is required. So, async: false solve your problem? – katiforis Nov 30 '17 at 20:03
  • @java-guy it didnt solve it, it get stuck at one request and keep sending it until i kill process – StillLearning Nov 30 '17 at 22:20
  • Yep, async: false pause user interface. So as an alternative you can simulate for loop. See my edited answer. – katiforis Nov 30 '17 at 23:19
  • Async true is by default – katiforis Dec 01 '17 at 04:29
  • @javaguy If OP has to do many individual calls, I did leave the suggesting in a comment on his question to look at using complete callbacks or promises. – Nope Dec 01 '17 at 09:18