admin管理员组

文章数量:1429135

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 plete? I'm unclear why it doesn't already, with the AJAX request specifically being declared as asynchronous.

Thanks in advance.

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 plete? I'm unclear why it doesn't already, with the AJAX request specifically being declared as asynchronous.

Thanks in advance.

Share Improve this question asked Sep 1, 2016 at 13:19 BirrelBirrel 4,8647 gold badges41 silver badges79 bronze badges 3
  • 3 your function ajaxCall doesn't return anything - which is equivalent to return undefined - you need to return something from the function (not the onreadystate callback) – Jaromanda X Commented Sep 1, 2016 at 13:22
  • Synchronous ajax calls really aren't a great idea -- what if the server has an issue? The JavaScript thread will just wait until the server responds, and so the page will bee pletely unresponsive to the user. There is never a good case for synchronous ajax. Don't use it. – Simba Commented Sep 1, 2016 at 13:28
  • I would remend trying out the use of "promises" (neat stuff). A brief explanation is given in the link posted in Jamiec's answer, otherwise check it out at MDN: developer.mozilla/en/docs/Web/JavaScript/Reference/… – Winter Commented Sep 1, 2016 at 13:36
Add a ment  | 

2 Answers 2

Reset to default 6

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?

The problem is that your return

return x.responseText.trim();

returns from the handler, but not from ajaxCall function - it has no return statement, so always returns undefined.

本文标签: