Birrel Birrel - 3 months ago 20
Ajax Question

JavaScript (Vanilla, no jQuery) - Function with *synchronous* AJAX (XMLHttpRequest) call behaves the same as async?

I have a file,

tst.html
with the content:

part two


(no markup or anything else, just for demonstration)

And then load said file, via synchronous AJAX (
XMLHttpRequest
):

function someFunc() {
var str = 'part one_';

var x = new XMLHttpRequest();
x.open('GET', 'tst.html', false); // "false" for synchronous
x.onreadystatechange = function() {
if(x.readyState === 4) {
switch(x.status) {
case 200:
str += x.responseText.trim();
break;

default:
return ''; // Or something
break;
}
}
}
x.send();

str += '_part three';

return str;
}


Calling the function:

alert(someFunc());

// Returns "part one_part two_part three"


which is the desired behavior.

But if I put the AJAX call into its own function:

function ajaxCall() {
var x = new XMLHttpRequest();
x.open('GET', 'tst.html', false);
x.onreadystatechange = function() {
if(x.readyState === 4) {
switch(x.status) {
case 200:
return x.responseText.trim();
break;

default:
return '';
break;
}
}
}

x.send();
}

function someFunc() {
var str = 'part one';

str += ajaxCall();

str += 'part three';

return str;
}


And then call it:

alert(someFunc());

// Returns "part one_undefined_part three"


The function returns the amalgamated string before the AJAX has a chance to finish, which is identical behavior to its asynchronous cousin.

I've been looking around for something along the lines of "Synchronous AJAX function," but nothing is turning up of any use.

The final use-case of the AJAX call is inside of a recursive set of functions, with further processing dependent on the AJAX return. Something like:

function one(url) {
var x = new XMLHttpRequest();
x.open('GET', url, false);
x.onreadystatechange = function() {
return two(x.responseText.trim());
}
x.send();
}

function two(str) {
var output;

output += stuff;

// ... parse through str
// ... until match found

if(isURL(match)) { // If `match` is a URL
output += one(match);
}else if(isFormattedString(match)) { // if `match` is a string
output += two(match);
}

output += more stuff;

// More processing of output

return output;
}

var final = one(url);


In the example above:


  • the system is always initiated with a URL (
    one(url)
    )

  • one()
    returns a string, which itself is the opening argument for
    two(str)

  • Within
    two()
    , the parser can encounter either


    1. another URL, or

    2. a parseable string.


  • Depending on which it is, one of the two functions is called

  • The output is added to the final result of the system



A callback from
one()
won't work on this either, because I still need to have a final
return
within
two()
.

function one(url, callback) {
// ... AJAX stuff
{
callback(two(x.responseText));
}
}

function two(str) {
// ... same as previous

// The following doesn't work, because then `two()` no longer has a `return`
// ... and any other code (i.e. for !isURL cases) will still continue to execute
if(isURL(match)) {
one(match, function(result) {
output += result;

// ... more processing

return output;
});
}else if(...) {
// ... same as previous
}

// ... more stuffs
}


The only other thing I'm finding is
deferred
, but I'm unsure how it would work with this either.

Is there a way to force JavaScript to treat this like other synchronous functions, where the executing code stops until the function is complete? I'm unclear why it doesn't already, with the AJAX request specifically being declared as asynchronous.

Thanks in advance.

Answer

I like your long, detailed question (wish everyone put as much effort into asking!), but in essence it boils down to this:

function ajaxCall() {
    var x = new XMLHttpRequest();
    x.open('GET', 'tst.html', false);
    x.onreadystatechange = function() {
        if(x.readyState === 4) {
            switch(x.status) {
                case 200:
                    return x.responseText.trim();
                    break;

                default:
                    return '';
                    break;
            }
        }
    }

    x.send();
}

Your return statement is a return from onreadystatechange, not ajaxCall as you expected. It differs only from your original in that your original was just concatenating strings. It has nothing to do with having moved it to its own function.

Dont use synchronous ajax! Understand how asynchronous functions work, especially How do I return the response from an asynchronous call?

Comments