How can I take advantage of callback functions for asynchronous XMLHttpRequest?

76,683

Solution 1

Callbacks are pretty simple and nifty! Because of the nature of AJAX calls, you don't block execution of your script till your request is over (it would be synchronous then). A callback is simply a method designated to handle the response once it gets back to your method.

Since javascript methods are first class objects, you can pass them around like variables.

So in your example

getText = function(url, callback) // How can I use this callback?
{
    var request = new XMLHttpRequest();
    request.onreadystatechange = function()
    {
        if (request.readyState == 4 && request.status == 200)
        {
            callback(request.responseText); // Another callback here
        }
    }; 
    request.open('GET', url);
    request.send();
}

function mycallback(data) {
   alert(data);
}

getText('somephpfile.php', mycallback); //passing mycallback as a method

If you do the above, it means you pass mycallback as a method that handles your response (callback).

EDIT

While the example here doesn't illustrate the proper benefit of a callback (you could simply put the alert in the onReadyStateChange function after all!), re usability is certainly a factor.

You have to keep in mind that the important thing here is that JS methods are first class objects. This means that you can pass them around like objects and attach them to all sorts of events. When events trigger, the methods attached to those events are called.

When you do request.onreadystatechange = function(){} you're just assigning that method to be called when the appropriate event fires.

So the cool thing here is that these methods can be reused. Say you have an error handling method that pops up an alert and populates some fields in the HTML page in the case of a 404 in the AJAX request.

If you couldn't assign callbacks or pass methods as parameters, you'd have to write the error handling code over and over again, but instead all you have to do is just assign it as a callback and all your error handling will be sorted in one go.


Solution 2

First of all I would suggest reading up on what a callback is. Here is a start.

The big picture

Callbacks are used extensively in asynchronous programming. When you don't want to block until a (possibly) long-running operation completes, one of the ways to approach the problem is to delegate the operation to someone who will do it on the side for you. This raises the question: how will you be able to tell when the operation is complete, and how will you get its results?

One solution would be to delegate the work to someone else and take a moment off your normal work every now and then to ask "is the work I gave you done yet?". If so, get the results in some way and off you go. Problem solved.

The problem with this approach is that it doesn't make your life much easier. You are now forced to ask every little while and you will not know that the operation is done as soon as it actually is (but only the next time you remember to ask). If you forget to ask, you will never be notified.

A better solution to this is the callback: when delegating work, provide a function along with it. The code which will actually do the work then promises to call that function as soon as the work completes. You can now forget all about that stuff and be secure in the knowledge that when the work is done, your callback will be called. No sooner, and no later.

What is the callback here?

In this specific case, callback is a function that you provide to getText as a manner of allowing it to communicate with you. You are in effect saying "do this work for me, and when you are finished, here's a function for you to call to let me know".

getText in fact chooses to use this callback only when the XMLHttpRequest (XHR) is completed, and at the same time it "lets you know" it passes you the contents of the HTTP response as well (so you can act upon that information).

Callbacks and more callbacks, oh my!

But take another moment to read the code. What is the value it stores to request.onreadystatechange? What is the purpose of request.onreadystatechange?

The answer is that request.onreadystatechange is there for you to populate with a callback. In effect, XHR gives you a way to provide it with a callback, and it promises to "call you back" whenever the state of the underlying HTTP request changes.

getText is a function that builds an abstraction on top of that: It plugs its own callback (an anonymous function -- I 'll refer to that as "inner") in there and accepts another callback from you (the parameter -- I 'll refer to it as "outer"). When the inner callback (which, remember: gets called whenever the state changes) detects that the state is "completed" (the meaning of the value 4) and the HTTP response status code is 200 (which means "OK"), it calls the outer callback to let you, the user of getText, know of the result.

I hope I 'm making sense. :)

Solution 3

Me personally I prefer to use Event Listener over callbacks.

Using Listeners comes handy especially when You're willing to process multiple asynchronous requests at once.

The Usage is as follows (taken from https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest)

function reqListener () {
  console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send()

Solution 4

XMLHttpRequest callback function and files upload with data array

function HttpPost(url, arr, cb, form){
    if (form === undefined) { var data = new FormData(); }else{ var data = new FormData(form); }
    if (arr !== undefined) {
        for (const index in arr) {    
            data.append(index, arr[index]);
        }
    }
    var hr = new XMLHttpRequest();        
    hr.onreadystatechange=function(){
        if (hr.readyState==4 && hr.status==200){
            if( typeof cb === 'function' ){ cb(hr.responseText); }
        }
    }
    hr.upload.onprogress = function(e) {
        var done = e.position || e.loaded, total = e.totalSize || e.total;
        console.log('xhr.upload progress: ' + done + ' / ' + total + ' = ' + (Math.floor(done/total*1000)/10) + '%');
    };
    hr.open("POST",url,true);
    hr.send(data);
}

// HttpPost callback
function cb_list(res){
    console.log(res);
    var json = JSON.parse(res);
    console.log(json.id + ' ' + json.list);
    // loop
    for (var objindex in json.list){
        console.log(json.list[objindex].id);
    }
}

Sample:

var data = [];
data["cmd"] = "get-cos";
var form = $('#form')[0];

HttpPost('/api-load', data, cb_list, form);

<form id="form" method="POST" enctype="multipart/form-data">
    <input type="file" name="file[]" multiple accept="image/*">
</form>

Http header content

hr.setRequestHeader("Content-Type", "application/json"); 
// data:
var json = {"email": "[email protected]", "password": "101010"}
var data = JSON.stringify(json);

hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// data: 
var data = "fname=Henry&lname=Ford";
Share:
76,683

Related videos on Youtube

Japboy
Author by

Japboy

I like open standards and semantic markup. Noob coder.

Updated on July 09, 2022

Comments

  • Japboy
    Japboy almost 2 years

    I'm currently writing JavaScript and confusing about callback. I've found it's not kind of built-in functions though...
    I'm now reading O'Relly JavaScript 5th Edition and it shows a sample code something like below:

    getText = function(url, callback) // How can I use this callback?
    {
        var request = new XMLHttpRequest();
        request.onreadystatechange = function()
        {
            if (request.readyState == 4 && request.status == 200)
            {
                callback(request.responseText); // Another callback here
            }
        }
        request.open('GET', url);
        request.send();
    }
    

    Basically, I suppose I don't understand the general idea of callback though... Could someone write a sample code to take advantage of callback above?

  • Japboy
    Japboy about 13 years
    This is simple and clear. In this case, reducing indentations and keeping code readability are the purpose of using callback function, I think. Is this right? Thanks anyway.
  • JohnP
    JohnP about 13 years
    nothing to do with indentation actually. Reusability certainly, since you can pass common functions as variables that will get acted on. I've expanded my answer a bit
  • Japboy
    Japboy about 13 years
    Thanks for your explanation! This is actually very easy to read and understand what callback is, even though my English skill is low :P onreadystatechange is also callback and, in fact, event driven programming needs deep nested proceeding when I loads many asynchronous proceedings. In this case, using callback function reduces indentations... Am I correct?
  • Japboy
    Japboy about 13 years
    Aw yes, you're right. Reusability must be the accurate word. Thanks :)
  • Japboy
    Japboy about 13 years
    s/reduces indentations/keeps reusability/
  • Waihon Yew
    Waihon Yew about 13 years
    @Japboy: It's not about reusability, although you do have reusability if you use the same callback on multiple occasions. It's about achieving asynchronous programming. See here (it's about async I/O, but most of it is applicable to any "work" not just I/O): en.wikipedia.org/wiki/Asynchronous_I/O
  • turbo2oh
    turbo2oh almost 11 years
    Well couldn't you just call the callback method explicitly onreadystatechange and still be asynchronous?
  • Waihon Yew
    Waihon Yew almost 11 years
    @turbo2oh: vs. what? Sorry, but I don't quite understand the question.
  • turbo2oh
    turbo2oh almost 11 years
    @Jon Looking at JohnP's example above, what would be the difference between mycallback(request.responseText); VS callback(request.responseText). The first scenario being no callback method was passed. Is the only difference scenario 2 is more dynamic?
  • Waihon Yew
    Waihon Yew almost 11 years
    @turbo2oh: Yes, #2 is configurable -- or conversely, #1 is useless except for one very specific task. For example, jQuery.get is useful only because it is configurable.
  • user.friendly
    user.friendly over 7 years
    Just learning about callbacks myself. To comment on "mycallback(request.responseText); VS callback(request.responseText)" regarding the OP's example in the question: If you specify the callback within the function, you take away your ability to pass in different callbacks/functionality. In the example, a url is passed in as well. You would most likely want a different callback function depending on what the value of that url is. If the callback was static in nature, you'd then be handling the responses from all urls the same way. Just my 2 cents.