10

I am working on a project where I have got 2 XMLHttpRequest() objects, say A and B.

What I want to accomplish is when A finish fetching a list of data items, B will be triggered to fetch some more items based on the previous data items fetch by A.

Currently my problem is that the two objects are working independent of one another.

My code is below:

            var A = new XMLHttpRequest();

            var B = new XMLHttpRequest();

            A.open("GET", directory, true);
            A.onreadystatechange = function () {

                if (A.readyState === 4) {
                    if (A.status === 200 || A.status == 0) {
                     //does... something
                     }
                }

            }
            A.send(null);
            while(true){

                B.open("GET", another_directory, false);
                B.overrideMimeType("application/document");
                B.send(null);
                if (B.status == "404")
                continue;

                 //does... something else
            }

This code is not working because I find evertime B proceed before A can complete. I basically don't know which event to use.

How can I accomplish my objective? What events can I use so that I can sync processing B right after finishing with A?

kishoredbn
  • 2,007
  • 4
  • 28
  • 47
  • 2
    Javascript is Asynchronous. You need to learn about callbacks... do A function, upon completion callback to B function... – zipzit Mar 14 '15 at 17:19
  • depending on what browser you're using and if what your working on will not be for use really soon, and especially if this is for yourself I'd suggest to learn to use `fetch`: http://updates.html5rocks.com/2015/03/introduction-to-fetch – Edwin Reynoso Mar 14 '15 at 17:20

3 Answers3

15

Ok, so let's start with your code. I've added a few comments to it, so now you can understand the source of the problem:

var A = new XMLHttpRequest(); //You create an XMLHttpRequest object
var B = new XMLHttpRequest(); //And an another

A.open("GET", directory, true); 

/* Now you open a GET request to DIRECTORY, with async TRUE. The third parameter can 
make a request sync or async, but sync is not recommended as described below. */

A.onreadystatechange = function () {
    if (A.readyState === 4) {
        if (A.status === 200 || A.status == 0) {

        /* So you registered an event listener. It runs when the readyState changes.
        You can use it to detect if the request is finished or not. If the readyState is
        4, then the request is finished, if the status code is 200, then the response is
        OK. Here you can do everythin you want after the request. */

         }
    }

}

A.send(null); //Now you send the request. When it finishes, the event handler will
// do the processing, but the execution won't stop here, it immediately goes to the 
// next function

while(true){ // Infinite loop
     B.open("GET", another_directory, false); //Open request B to ANOTHER_DIRECTORY,
     // but now, request B will be synchronous

     B.overrideMimeType("application/document"); // Configure mime type

     B.send(null); // Send the request

     if (B.status == "404")
         continue;
         // If it's not found, then go to the next iteration

     // and do something else
}

I hope that now you can see the source of the problem. When you run this script, then your start an async request and then immediately start the next one. Now you can choose from 2 ways.

Run next request from callback (recommended)

It's the better way. So start your first (async) request and in the event listener (where you do the processing) you can start the next request. I've made a commented example here: http://jsfiddle.net/5pt6j1mo/1/

(You can do it without arrays - it was just an example)

If you use this way then the GUI won't freeze until you are waiting for response. Everything will be responsible so you can interact with the page, you can create cancel button, etc.

Synchronous AJAX (not recommended)

I don't recommend it because "Synchronous XMLHttpRequest on the main thread is deprecated" in Chrome, but if you really want to then you can try to use this solution. So an XMLHttpRequest's open function has 3 arguments:

  • METHOD: which HTTP methid to use
  • URL: which URL to request
  • ASYNC: Asynchronous request? If false then it will be synchronous wich means that after you call .send(), it will pause execution until the response comes back.

So if you set the third parameter to FALSE then you can easily do it... but you shouldn't!

edemmester
  • 196
  • 6
11

Here is an alternative solution, either use the fetch API or promisify native XHR and this problem becomes much simpler:

fetch(directory).then(function(response){
    // some processing
    return fetch(another_directory); // can change content type too, see the mdn docs
}).then(function(responseTwo){
      // all processing is done
}).catch(function(err){
      // handle errors from all the steps above at once
});

This is just as native as XHR, and is much much simpler to manage with promises.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
4

(After lengthy edit) I'd recommend strongly that you take the time to understand the nature of asynchronous calls within JavaScript. Here's a bit of recommended reading.Asynchronous Programming in JavaScript I think that is simple enough to understand what is going on. Note: Stop reading at "Enter Mobl".

In JavaScript when you call a function, the system places that function into a 'queue' with an implicit instruction to go ahead and run it as soon as you can. It does that for each and every function call. In your case you are telling the system to run A, then run B. A goes in the queue, B goes in the queue. They are submitted as individual functions. B happens to run first.

For normal functions, if you want to control the sequence, you can nest the A function call within the B function call. But oops. You are using XMLHttpRequest, so that limits your ability to customize the functions. Read on. Check out Ajax Patterns on the subject Look at the paragraph for "Asynchronous Calls". Look at your code...

A.onreadystatechange = function () {
    if (A.readyState === 4) {
         if (A.status === 200 || A.status == 0) {
             //does... something
             (RUN ALL THE B.methods right here...)
         }
    }
}

I think that will get you to your destination, assuming you want a no jQuery solution.

For the person who just wants a functioning system, and doesn't want to understand the language better, here is a jquery solution... Note how the B function call is nested within the A function call. Do note that the order of this nesting is based on the presence of the jQuery success tag. If not using jQuery, you will manually have to nest the functions as appropriate.

var special_value;
$("button").click(function(){
    $.ajax({url: "demo_testA.html", 
            type: 'GET',
            success: function(resultA){
               special_value = resultA;
               $.ajax({url: "demo_testB.html",
                      type: 'GET', 
                      data: special_value, 
                      success: function(resultB){
                            $("#div1").html(resultB);
               }});
    });
});

I will say, it would be much easier to help you help yourself with the use of better communications. If you don't like something, then so state. If you don't understand something ask for more clarification or edit your problem statement. Feedback is a good thing.

zipzit
  • 3,778
  • 4
  • 35
  • 63
  • 5
    The question was not tagged jQuery, and this answer fails to give any explanation of the problems or the suggested solution. – Bergi May 03 '15 at 10:05
  • Gee, I thought it was obvious that the problem is 98% comprehension of javascript function calls and 2% use of XMLHttpRequest(). Read my comment below the original question. It was not clear that ikis intended a no jQuery solution. Check out his return comments to my solution.. ooops. He never did provide any feedback to the answer I made three weeks ago. Instead, he added a bounty (which never did propogate a message to me? er.. why not?) On the tags... have you ever seen questions with poor tag choices? (ans. All the time) He could have easily mentioned no-jquery rqmt in his question. – zipzit May 04 '15 at 16:10
  • 1
    Sure. But your answer doesn't provide any comprehension of (async) function calls, just a jQuery example snippet. A valid answer doesn't need to inclue XHR, and can contain jQuery; but it mustn't rely on jQuery. – Bergi May 04 '15 at 16:25
  • Hence, the jquery solution. I was trying to make the elegant statement "get off your butt, learn how the language works", and I wasn't about to nest the functions for him (which would have taken me about four minutes). So yeah, it doesn't say that message very well, and for that I apologize. But had he commented back (which is what I was looking for...) I would have given him more hints to enable him to get to the correct destination on his own. So I get downgraded. Okay, whatever. I'm still not going to do this guy's (home)work for him. – zipzit May 04 '15 at 16:40
  • 12
    Yeah, if you feel that OP is not willing to communicate, just *dont answer* and move on. You might want to delete your post if you don't intend to improve it. – Bergi May 04 '15 at 16:43
  • Your jQuery solution is pretty bad too - it's _bad_ jQuery. There is no need for double nesting here – Benjamin Gruenbaum May 05 '15 at 17:10
  • Not sure I understand "bad jQuery"... "sloppy javascript" in relationship to double nesting, certainly, yes. Can you explain? Is there a standard or best practices reference I should catch up on? (Or are we talking of Promises and coherent error handling?). Thx. – zipzit May 05 '15 at 23:03
  • @zipzit well, `$.ajax` returns a jqXHR object which is a promise. You can rewrite the above code as https://gist.github.com/benjamingr/bf199d640053ea497fde – Benjamin Gruenbaum May 06 '15 at 10:30