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

JqueryAjax

Jquery Problem Overview


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;
            }
        }
    });
});

Jquery Solutions


Solution 1 - Jquery

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.

Solution 2 - Jquery

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("...");

Solution 3 - Jquery

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

async: false

Solution 4 - Jquery

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

Solution 5 - Jquery

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.

Solution 6 - Jquery

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();

Solution 7 - Jquery

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.

Solution 8 - Jquery

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');

Solution 9 - Jquery

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);	
}

Solution 10 - Jquery

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHobhouseView Question on Stackoverflow
Solution 1 - JquerycgpView Answer on Stackoverflow
Solution 2 - JqueryMilMikeView Answer on Stackoverflow
Solution 3 - JqueryidrosidView Answer on Stackoverflow
Solution 4 - JquerykofifusView Answer on Stackoverflow
Solution 5 - JquerysamuelagmView Answer on Stackoverflow
Solution 6 - JqueryTaohidul IslamView Answer on Stackoverflow
Solution 7 - JqueryFrank HoffmanView Answer on Stackoverflow
Solution 8 - Jqueryuser7793758View Answer on Stackoverflow
Solution 9 - JqueryHassan EjazView Answer on Stackoverflow
Solution 10 - JqueryStuView Answer on Stackoverflow