ChatGPT解决这个技术问题 Extra ChatGPT

How do I make jQuery wait for an Ajax call to finish before it returns?

I have a server side function that requires login. If the user is logged in the function will return 1 on success. If not, the function will return the login-page.

I want to call the function using Ajax and jQuery. What I do is submit the request with an ordinary link, with a click-function applied on it. If the user is not logged in or the function fails, I want the Ajax-call to return true, so that the href triggers.

However, when I use the following code, the function exits before the Ajax call is done.

How can I redirect the user gracefully to the loginpage?

$(".my_link").click(
    function(){
    $.ajax({
        url: $(this).attr('href'),
        type: 'GET',
        cache: false,
        timeout: 30000,
        error: function(){
            return true;
        },
        success: function(msg){ 
            if (parseFloat(msg)){
                return false;
            } else {
                return true;
            }
        }
    });
});
Might be an old thread, but as @kofifus points out setting async to false is a bad design and "no timeouts etc will be processed". Might be try this simplified solution - stackoverflow.com/a/11576418/6937841
You can change return true; to window.location.href = url; and it would gracefully redirect to the login page once ajax call is finished.

m
mmv_sat

If you don't want the $.ajax() function to return immediately, set the async option to false:

$(".my_link").click(
    function(){
    $.ajax({
        url: $(this).attr('href'),
        type: 'GET',
        async: false,
        cache: false,
        timeout: 30000,
        fail: function(){
            return true;
        },
        done: function(msg){ 
            if (parseFloat(msg)){
                return false;
            } else {
                return true;
            }
        }
    });
});

But, I would note that this would be counter to the point of AJAX. Also, you should be handling the response in the fail and done functions. Those functions will only be called when the response is received from the server.


Passing along good practices, in my opinion, isn't judging, and is the mark of some of the best answers here on StackOverflow.
Never use async: false. The browser's event loop will hang while waiting on unreliable network I/O. There's always a better way. In this case, the link's target can verify the user session and 302 to the login page.
For testing, async: false can be very useful.
@Matthew Really? What is the alternative to sending an ajax with async before sending user to other page or refreshing?
async with value false is gone deprecated in most browser use cases. It may throw exceptions in newer version of browsers. See xhr.spec.whatwg.org/#sync-warning (applies to async parameter of xhr open method, which is what uses jQuery).
f
fragilewindows

I am not using $.ajax but the $.post and $.get functions, so if I need to wait for the response, I use this:

$.ajaxSetup({async: false});
$.get("...");

i
idrosid

The underlying XMLHttpRequest object (used by jQuery to make the request) supports the asynchronous property. Set it to false. Like

async: false

d
doydoy44

Instead of setting async to false which is usually bad design, you may want to consider blocking the UI while the operation is pending.

This can be nicely achieved with jQuery promises as follows:

// same as $.ajax but settings can have a maskUI property
// if settings.maskUI==true, the UI will be blocked while ajax in progress
// if settings.maskUI is other than true, it's value will be used as the color value while bloking (i.e settings.maskUI='rgba(176,176,176,0.7)'
// in addition an hourglass is displayed while ajax in progress
function ajaxMaskUI(settings) {
    function maskPageOn(color) { // color can be ie. 'rgba(176,176,176,0.7)' or 'transparent'
        var div = $('#maskPageDiv');
        if (div.length === 0) {
            $(document.body).append('<div id="maskPageDiv" style="position:fixed;width:100%;height:100%;left:0;top:0;display:none"></div>'); // create it
            div = $('#maskPageDiv');
        }
        if (div.length !== 0) {
            div[0].style.zIndex = 2147483647;
            div[0].style.backgroundColor=color;
            div[0].style.display = 'inline';
        }
    }
    function maskPageOff() {
        var div = $('#maskPageDiv');
        if (div.length !== 0) {
            div[0].style.display = 'none';
            div[0].style.zIndex = 'auto';
        }
    }
    function hourglassOn() {
        if ($('style:contains("html.hourGlass")').length < 1) $('<style>').text('html.hourGlass, html.hourGlass * { cursor: wait !important; }').appendTo('head');
        $('html').addClass('hourGlass');
    }
    function hourglassOff() {
        $('html').removeClass('hourGlass');
    }

    if (settings.maskUI===true) settings.maskUI='transparent';

    if (!!settings.maskUI) {
        maskPageOn(settings.maskUI);
        hourglassOn();
    }

    var dfd = new $.Deferred();
    $.ajax(settings)
        .fail(function(jqXHR, textStatus, errorThrown) {
            if (!!settings.maskUI) {
                maskPageOff();
                hourglassOff();
            }
            dfd.reject(jqXHR, textStatus, errorThrown);
        }).done(function(data, textStatus, jqXHR) {
            if (!!settings.maskUI) {
                maskPageOff();
                hourglassOff();
            }
            dfd.resolve(data, textStatus, jqXHR);
        });

    return dfd.promise();
}

with this you can now do:

ajaxMaskUI({
    url: url,
    maskUI: true // or try for example 'rgba(176,176,176,0.7)'
}).fail(function (jqXHR, textStatus, errorThrown) {
    console.log('error ' + textStatus);
}).done(function (data, textStatus, jqXHR) {
    console.log('success ' + JSON.stringify(data));
});

And the UI will block until the ajax command returns

see jsfiddle


Doesn't setting async to false do the exact same thing except in a single line of code?
Not at all. Setting async to false make the request async - that is halts processing until it returns which is usually bad practice, for example no events, other ajax requests, timeouts etc will be processed. You can also modify the code above to block only part of the UI while your ajax is processing (ie the part it will affect)
A
Anthony Grist

I think things would be easier if you code your success function to load the appropriate page instead of returning true or false.

For example instead of returning true you could do:

window.location="appropriate page";

That way when the success function is called the page gets redirected.


This is the real answer to the question.
T
Taohidul Islam

In modern JS you can simply use async/await, like:

  async function upload() {
    return new Promise((resolve, reject) => {
        $.ajax({
            url: $(this).attr('href'),
            type: 'GET',
            timeout: 30000,
            success: (response) => {
                resolve(response);
            },
            error: (response) => {
                reject(response);
            }
        })
    })
}

Then call it in an async function like:

let response = await upload();

Your code won't work... you're on the right track but as it is, it won't work.
Can you please elaborate more? After testing the code I've answered this question, it should work.
await can't be used outside of the async function, it throws an error.
Yes, I've mentioned it in the second last line of my answer, "Then call it in an async function".
It's a great solution but to help others looking for a great solution (like I was), you should edit your code and show it in a more complete way. Perhaps even include a link to a fiddle. That would be awesome!
F
Frank Hoffman

Since I don't see it mentioned here I thought I'd also point out that the jQuery when statement can be very useful for this purpose.

Their example looks like this:

$.when( $.ajax( "test.aspx" ) ).then(function( data, textStatus, jqXHR ) {
  alert( jqXHR.status ); // Alerts 200
});

The "then" part won't execute until the "when" part finishes.


u
user7793758

since async ajax is deprecated try using nested async functions with a Promise. There may be syntax errors.


async function fetch_my_data(_url, _dat) {

   async function promised_fetch(_url, _dat) {

      return new Promise((resolve, reject) => {
         $.ajax({
            url:  _url,
            data: _dat,
            type: 'POST',
            success: (response) => {
               resolve(JSON.parse(response));
            },
            error: (response) => {
               reject(response);
            }
         });
      });
   }

   var _data = await promised_fetch(_url, _dat);
   
   return _data;
}

var _my_data = fetch_my_data('server.php', 'get_my_data=1');


H
Hassan Ejaz

It should wait until get request completed. After that I'll return get request body from where function is called.

function foo() {
    var jqXHR = $.ajax({
        url: url,
        type: 'GET',
        async: false,
    });
    return JSON.parse(jqXHR.responseText);  
}

Please explain your solution.
Added @Dropout.
Thanks @Hassan Ejaz! Answers which do not have an explanation and are only code get flagged as low effort.
S
Stu

The original question was at today's date asked 12 years ago and was 'How do I make jQuery wait for an Ajax call to finish before it returns?' jQuery has come a long way since then.

There are a few solutions mentioned above and I couldn't get any of them to work with the latest version of jQuery: $.when().then.() doesn't seem to be synchronous unless its uses 'async: false' which is no longer supported, so doesn't work in newer versions of jQuery.

But promises are built into jQuery ajax calls so it shouldn't be that difficult to make ajax calls synchronous.

I use namespaced js functions so the example below is in that format. The example is for custom form validation that calls the server to validate that user input does not attempt to duplicate an existing item.

This code will probably not work in IE or Legacy Edge unless using Babel, but I tend to block those browsers as they are no longer supported by Microsoft.

///Namespace validate
check: async function(settings){
    let IsValid = false;
    let Message = ''
    let data = await validate.serverCheck('function', value);
    IsValid = data.OK;
    Message = data.Message;
}

serverCheck: async function (fn, value) {
    var request = {
        validateValue: $.sanitize(value)
    };

    let result;

    try {
            result = await $.ajax({
                dataType: "json",
                type: "post",
                url: "/api/validate/" + fn + "/",
                data: request
        });

        return result;
    } catch (x) {}
}

and there you have it