javascriptcallbackonreadystatechange

return value from onreadystatechange with callback


I'm trying to return a value from an onreadystatechange AJAX call... I found this page : stackoverflow link. I though I had it working but realised that it made no difference to add or remove the fn function. The following code works :

username_is_available();

function username_is_available() {
  var username = document.getElementById('username').value;

  get_data('username', username, function(returned_value) {
   if (returned_value == 'true') {
     document.getElementById('username_err').innerHTML = 'Taken';
    } else {
     document.getElementById('username_err').innerHTML = 'Available';
    };
  });
}

function get_data(data_type, data, fn) {
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
  if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    fn(xmlhttp.responseText);
  }
};
  xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
  xmlhttp.send();
}

It all works fine but that's not my goal, I would like a function username_is_available() that returns true if the username is indeed available. Instead, here I an action happens (innerHTML is changed). And if I try and do a return in the anonymous function I get the same result as if I had returned it from directly inside the onreadystatechange : var unasigned


Solution

  • Unfortunately, since the process to determine if a username is taken is asynchronous, there is no way to simply return a value of true or false from the function call. What you can do is set up something similar to what you have now (callbacks) using language features specifically designed for this exact purpose.

    A Promise is one of these features.

    Usage would look something roughly like this:

    function username_is_available(username) {
        return new Promise(resolve => {
            get_data("username", username, resolve);
        });
    }
    
    function get_data(data_type, data, fn) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                fn(xmlhttp.responseText == "true");
            }
        };
        xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
        xmlhttp.send();
    }
    
    // Usage:
    username_is_available("Ivan").then(available => {
        let text = available ? "Available" : "Taken";
        document.getElementById("username_err").innerHTML = text;
    });
    

    This relies on availablity.php returning true and false as text, which is converted to a Boolean before resolve is called.

    In the future, when ES7+ async and await directives are available, using the promise will be as simple as this (note the await keyword):

    let available = await username_is_available("Ivan");
    let text = available ? "Available" : "Taken";
    document.getElementById("username_err").innerHTML = text;
    

    Edit: If you can't use ES6 or promises, it's back to good ol' callbacks!

    function username_is_available(username, callback) {
        get_data("username", username, callback);
    }
    
    function get_data(data_type, data, fn) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                fn(xmlhttp.responseText == "true");
            }
        };
        xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
        xmlhttp.send();
    }
    
    // Usage:
    username_is_available("Ivan", function(available) {
        var text = available ? "Available" : "Taken";
        document.getElementById("username_err").innerHTML = text;
    });