What I'm Trying To Accomplish
I need to trigger 1 to 3 different $.post() requests, and whether it's 1 call, 2 consecutive calls, or 3 consecutive calls is decided by some basic user selection. Each call must only start after the previous is completely finished.
I am dealing with 3 simple behavior cases -- the user, being presented with "Checkbox 1," "Checkbox 2," and a "Continue" button," opts to
Select nothing and then press "Continue" button, which makes an XHR call to '/remote.php',
The user opts to only select "Checkbox 1" or "Checkbox 2," and then presses "Continue" button, which calls $.post() Function 1 that is bound to Checkbox 1, or $.post() Function 2 that is bound to Checkbox 2, and then makes an XHR call to '/remote.php',
Or the user selects both Checkbox 1 + 2 and then presses Continue, which calls $.post() Function 1, then calls $.post() Function 2, and then makes an XHR call to '/remote.php'.
I need to make sure that the Continue-button $.post() function does not fire until the Checkbox-bound $.post() functions fire and complete.
The Problem
The problem is that if Checkbox 1 is selected and Checkbox 2 is selected, and then the Continue button is pressed, as I understand it, the loading order should be:
Checkbox 1 bound $.post() request fires and completes, then
Checkbox 2 bound $.post() request fires and completes, then
Continue button bound $.post() fires, and pages is changed via AJAX at the end of the function tied to "Continue" button bound function.
So, where the result should look like:
XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/cart.php?action=add&product_id=1284".
XHR finished loading: POST "/remote.php".
It instead often comes out like this:
XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/remote.php".
XHR finished loading: POST "/cart.php?action=add&product_id=1284".
So when the page changes due to the AJAX at the end of the "last"/"Continue-button function, either neither of the Checkbox 1 or Checkbox 2 actions have taken place, or one of the two or both do register in the backend (ie, added to cart) but do not reflect in the AJAXified DOM as they should as the final AJAX fires and completes before the previous $.post() calls have completed.
My Code
The HTML
The HTML is basic:
<form method="post" action="#" onsubmit="newChooseShippingProvider(); return false;">
<label for="delSigCheck" class="del-sig-text"><input id="delSigCheck" type="checkbox" onchange="addDelSigToCart();" title="Add Delivery Signature"></label>
<label for="addInsCheck" class="ins-add-calc"><input id="addInsCheck" type="checkbox" onchange="addInsToCart();" title="Add Delivery Signature" data-ins-id="1284"></label>
<input type="submit" value="Continue" class="btn Small">
</form>
The Javascript/jQuery
This is my latest--4th or 5th--attempt, and still does not work:
function addDelSigToCart() {
$('#delSigCheck').toggleClass('checked');
}
function addInsToCart() {
$('#addInsCheck').toggleClass('checked');
}
function newChooseShippingProvider() {
var originalCheckout = ExpressCheckout.ChooseShippingProvider();
if ($('.ShippingProviderList .radio span').hasClass('checked')) {
var addInsCheck = $('#addInsCheck').hasClass('checked');
var delSigCheck = $('#delSigCheck').hasClass('checked');
var insId = $('#addInsCheck').attr('data-ins-id');
var addDelSigUrl = '/cart.php?action=add&product_id=1280';
var addInsUrl = '/cart.php?action=add&product_id=' + insId;
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl, function() {
$.post(addInsUrl, function() {
originalCheckout;
});
});
} else if (!delSigCheck && !addInsCheck) {
originalCheckout;
} else if (delSigCheck && !addInsCheck) {
$.post(addDelSigUrl, function() {
originalCheckout;
});
} else if (!delSigCheck && addInsCheck) {
$.post(addInsUrl, function() {
originalCheckout;
});
}
} else {
originalCheckout;
}
What I've Tried
I've gone through several version of chaining the $.post() calls, but nothing seems to work consistently.
What I am using now and what seems to work the best for me with extensive testing is using setTimeout to chain the function with some delay, like this:
...
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl);
setTimeout(function() {
$.post(addInsUrl);
setTimeout(function() {
ExpressCheckout.ChooseShippingProvider();
}, 1300);
}, 1300);
} else if ...
And this version above is what I'm using now, as it seems to give the most consistent results, seeing the scripts load typically as 1,2,3, followed by a DOM AJAXified with appropriate changes based on function 1 and 2. However, I don't think the setTimeout is working as even when I increase it to 5000 or 10000, the action is performed "instantaneously" and no delay takes place (at least certainly nothing close to 5-10 seconds).
I've also tried putting the functions inside $.post()'s success callback:
...
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl, function() {
setTimeout(function() {
$.post(addInsUrl, function() {
setTimeout(function() {
originalCheckout;
}, 1300);
});
}, 1300);
});
} else if ...
And finally I've also tried:
$.when($.post(addDelSigUrl)).then(originalCheckout);
as well as .done and success: but none of it works, and the $.posts()'s load in an unexpected order, failing.
The Question
What am I doing wrong?
How can I make it so 1 loads fully, then 2 loads fully, and only then 3 fires and loads?
UPDATE 1:
I just tried jfriend00's answer:
$.post(addDelSigUrl, { cache: false }).then(function(data1) {
//CONSOLE.LOGing HERE
console.log(data1);
return $.post(addInsUrl, { cache: false });
}).then(function(data2) {
//CONSOLE.LOGing HERE
console.log(data2);
return originalCheckout;
});
But it still resulted in:
XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/remote.php".
XHR finished loading: POST "/cart.php?action=add&product_id=1284".
and both console.logs fire immediately after the first "XHR ...", THEN /remote.php fires (though it should fire last as part of originalCheckout), THEN the 3rd XHR fires.
UPDATE 2
Now that we got the XHRs firing and loading in the correct order via .then(), the second part of the problem I am having is that the 3rd XHR to /remote.php updates the DOM via AJAX with data from the backend. Part of that data is the 1st and 2nd $.posts.
I think the 3rd AJAX call is firing and completing milliseconds before some action is taken on the backend via server-side PHP, and because of this more than 50% of the time, the DOM update via the 3rd AJAX call is missing the data from the 1st and/or 2nd call (most often the DOM changes include Checkbox 1/AJAX call 1, but not 2).
How can I fix this? I've tried setTimeout but it doesn't seem to work as even when I set it to like 30000, the 3rd AJAX fires as soon as the 1st/2nd complete.
Latest front-end code:
function newChooseShippingProvider() {
if ($('.ShippingProviderList .radio span').hasClass('checked')) {
var addInsCheck = $('#addInsCheck').hasClass('checked');
var delSigCheck = $('#delSigCheck').hasClass('checked');
var insId = $('#addInsCheck').attr('data-ins-id');
var addDelSigUrl = '/cart.php?action=add&product_id=1280';
var addInsUrl = '/cart.php?action=add&product_id=' + insId;
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl).then(function(data1) {
return $.post(addInsUrl);
}).then(function(data2) {
return ExpressCheckout.ChooseShippingProvider();
});
} else if (!delSigCheck && !addInsCheck) {
ExpressCheckout.ChooseShippingProvider();
} else if (delSigCheck && !addInsCheck) {
$.post(addDelSigUrl).then(function(data1) {
return ExpressCheckout.ChooseShippingProvider();
});
} else if (!delSigCheck && addInsCheck) {
$.post(addInsUrl).then(function(data1) {
return ExpressCheckout.ChooseShippingProvider();
});
}
} else {
ExpressCheckout.ChooseShippingProvider();
}
}
The simplest way to sequence jQuery ajax operations is to use the built-in promises:
$.post(...).then(function(data1) {
return $.post(...);
}).then(function(data2) {
return $.post(...);
}).then(function(data3) {
// everything done here
});
Working demo that shows you the precise sequencing: http://jsfiddle.net/jfriend00/zcfr2xy0/
OK, it appears that the problem is that you're doing this:
var originalCheckout = ExpressCheckout.ChooseShippingProvider();
And, then you think that sometime later, you can just do:
originalCheckout;
and that will somehow execute the former. That's not the case. Your ExpressCheckout.ChooseShippingProvider() function is executed immediately and the return result from executing that function is assigned to originalCheckout.
You simply can't do it that way. When you have () after a function name, that means to execute it NOW. I would suggest that you just replace all instances of originalCheckout; with ExpressCheckout.ChooseShippingProvider();.
If you "chain" AJAX post operations (meaning that you do your process once receiving the data from the previous call) then nothing strange should happen.
From the symptom I'd think more to some cache-related problem. Adding an extra random value to the query is a quick'n dirty way to get rid of whoever is caching the result and responding instead of who should. Also using a POST request instead of a GET (if possible) may help on this issue and better conveys the idea that the operation is a mutation that should not be skipped or done out of order.
Note that a stale response problem could be at several levels: browser, proxy, web server, cms plugin...
Related
In my javascript code when someone change option and click on Go. it's change property for multiple items. I see the save button and edit button for every item.
so I manually clicked the button after change the select and on the click of Go.
for (var i = 0; i < checked.length; i++) {
var cur = checked[i];
$(cur).parents('tr').find('.transaction-category-wrapper .select2').val(catId);
$(cur).parents('tr').find('.transaction-verify-wrapper .btn-save').click();
}
Now problem is I want to refresh the page, but after making sure that response has been come to the page. How I can achieve it.
I am thinking to implement the setTimeout but it wouldn't be good option in case of server doesn't has executed and timeout just refresh the page.
Is jQuery have some property which let me know that javascript request has been complete and response has been received.
$(document).on('click', '.btn-save', function () {
// val logic
$.post("/Entries/AddEntries", val, function (r) {
enableTooltip();
});
});
Since every click generates a post request. You need to keep track of all those requests in an array and then wait for them to resolve. So, your code should look like:
var requests = [];
$(document).on('click', '.btn-save', function () {
// val logic
requests.push($.post("/Entries/AddEntries"));
});
Promise.all(requests).then((data) => {do something});
You can use .then to handle success and failure cases of your AJAX requests:
$.post("your_url", { ... })
.then(function() {
// If succeeded
}, function() {
// If failed
});
.post returns a jQuery deferred object, which can be responded with a .then() handler. The first argument of .then is considered as a success handler, and the second one is a failure handler.
In case of multiple AJAX calls, you can use $.when to take action when all the AJAX calls are done.
$.when($.post( ... ), $.post( ... ))
.then(function() {
// If all AJAX calls succeed
}, function() {
// If any of the call fails
});
When the city input field is blurred I get somnething via an ajax request and set that as the value of a hidden field in the same form that the city field resides in.
$('input#city').on('blur', function() {
$.ajax({
url: 'get/something?param=val',
success: function(response) {
$('input:hidden[name="something"]').val(response);
}
});
});
If the user submits the form immediately after blurring off the city field sometimes due to latency the hidden field is not populated because the SQL on the other end is taking too long.
The form that both these fields are in is also submitted via ajax:
$('form#find-users').on('submit', function() {
if(NO_AJAX_CURRENTLY_RUNNING_ON_PAGE) {
// do stuff
}
});
How to detect if no ajax is running on the page? This will ensure that the city ajax was completed and the hidden field populated before the form is processed.
EDIT
Actually it won't, it will only prevent the form from being submitted. But if I can detect that then I can use a setInterval and keep trying to run that code until it runs because ajax is complete. Ideally there will be something in jQuery that waits until other ajax is complete and then submits.
Use jQuery's Ajax Events. As long as all of your Ajax calls are generated using jQuery, you have a way of knowing if any Ajax calls are outstanding.
$(document).ready(function() {
var ajaxBusy = false;
$(document).ajaxStart( function() {
ajaxBusy = true;
}).ajaxStop( function() {
ajaxBusy = false;
});
});
Edit:
So that answers your direct question about "How do I know if there is any Ajax call running."
Alternatively, you could disable the form's submit buttons when run your blur handler, and then re-enable it when you're done.
$('input#city').on('blur', function() {
var submit = $(this).closest('form').find(':submit:enabled');
submit.prop('disabled', true);
$.ajax('get/something?param=val').done(function(response) {
$('input:hidden[name="something"]').val(response);
}).always(function() {
submit.prop('disabled', false);
});
});
Edit 2:
So now we're at the point where we would like to delay the form submission until all current Ajax calls have completed. We let people click on the submit button, but if there are pending Ajax calls we don't do anything right away.
We can use a Deferred object to help us with this.
$(document).ready(function() {
var ajaxDefer = $.Deferred().resolve();
$(document).ajaxStart( function() {
ajaxDefer = $.Deferred();
}).ajaxStop( function() {
ajaxDefer.resolve();
});
$('form#find-users').on('submit', function() {
ajaxDefer.always(function() {
// Code here will always be executed as soon as there are no
// Ajax calls running.
// this points to the deferred object (ajaxDefer), so use the closure
// to carry over any variables you need.
});
});
});
When we're just starting out, we set up our ajaxDefer object in a resolved state. That means any functions attached using .always() will execute immediately.
When the first Ajax call starts, we replace the old ajaxDefer object with a new one that has not been resolved. Any new functions attached using ajaxDefer.always() will be deferred until later.
When the last Ajax call completes, we call ajaxDefer.resolve(), which causes any unexecuted deferred functions to execute. Now we're back to our initial state, where any newly-attached functions will execute immediately.
When somebody tries to submit the form, create an anonymous function that does the work and attach it to ajaxDefer. It will get executed when appropriate, depending on if there are any outstanding Ajax requests or not. Be mindful of your closures.
Use this to check if AJAX calls are currently in-progress using JQuery:
if ($.active == 0) {
...
}
you can put a variable in the global namespace, perhaps named ajaxLock and toggle it on when AJAX starts and off when the response comes. Then check it before allowing submit.
something like
var ajaxLock = 1;
$('input#city').on('blur', function() {
$.ajax({
url: 'get/something?param=val',
success: function(response) {
$('input:hidden[name="something"]').val(response);
ajaxLock = 0;
}
});
});
Use a lock variable like you suggested:
$('input#city').on('blur', function() {
window.AJAX_CURRENTLY_RUNNING_ON_PAGE = true;
$.ajax({
url: 'get/something?param=val',
success: function(response) {
$('input:hidden[name="something"]').val(response);
},
complete: function() { window.AJAX_CURRENTLY_RUNNING_ON_PAGE = false; }
});
});
$('form#find-users').on('submit', function() {
if(window.AJAX_CURRENTLY_RUNNING_ON_PAGE) {
return;
}
//dostuff
});
What i could have done on this circumstances is to use plugin like block ui or disable the form submit button,the reason is you need to be interactive in your design,you may well able to lock the form submission,but its better to give a message or have a modal gray out
Is there any way to time how long a jquery ajax request has been going on? sometimes searches take too long and it would be nice to add a jquery abort() button if the search takes over, say, 5 seconds. Any way I can do this!
On the other end of the ajax request is a php file that makes a postgresql request.
Much thanks for any ideas!
Take a look at the timeout option (http://api.jquery.com/jQuery.ajax/). You can set it on a particular call, or globally with $.ajaxSetup().
To have the abort button appear after 5 seconds, add a setTimeout function after your call to send. Once the AJAX command is complete, you can add code to clear the timeout and remove the abort button if it exists.
var timeOutID = 0;
$.ajax({
url: 'ajax/test.html',
success: function(data) {
clearTimeOut(timeOutID);
// Remove the abort button if it exists.
}
});
timeOutID = setTimeout(function() {
// Add the abort button here.
}, 5000);
This way the abort button will never appear if AJAX returns quick enough.
Usually, I'll set a timeout once the request is sent that will trigger after 10 seconds or so and then fallback on something else to make sure it still happens (for example, form submission).
So set a variable to false, var failed = false; and do the request
At the same time that the request starts, set a timeout:
setTimeout(function() {
failed = true;
$("#form").submit();
return false;
}, 10000);
In the return function of the ajax call, check to see if the failed variable has been set to true, and if it has, don't actually do whatever it was originally trying, otherwise it could mess something up, or confuse the user if something else is happening (since these things usually happen on slower internet connections, if the next step appears while a new page is loading, they might try to interact and then the page will change).
$.post("ajaxcall.php", {'etc': "etc"},
function(returned) {
if (failed != true) {
//do whatever with returned variable
}
});
var timer = 0,
XHR = $.ajax({
url: 'ajax/mypage.html',
beforeSend: function() {
timer=setTimeout(showAbort, 5000);
}
});
function showAbort() {
$('<input type="button" value="Abort" id="abort_button"/>').appendTo('#some_parent');
$('#abort_button').on('click', function() {
XHR.abort(); //abort the Ajax call
});
}
XHR.always(function() { //fires on both fail and done
clearTimeout(timer);
if ($('#abort_button').length) {
$('#abort_button').remove(); //remove button if exists
}
});
I have looked in lots of places for this, and I'm probably being an idiot because this is done a lot, but here's my situation.
I am trying to display a checkbox next to an e-mail field on my website iff the e-mail has not been used to register already.
What I have is something like this:
$('[name=reg_email]').change( function() {
if(!emailUsed()) {
//Update image to be a green checkmark
} else {
//Update image to be a huge red X
}
}
And my "emailUsed" function should be returning a Javascript boolean variable depending on whether or not the e-mail address is in the database. To facilitate this, I've created a script which will determine if it's there or not. So the emailUsed() function just needs to call the script and return, but I need to wait until readystate == 4 before I return, and all of the methods I have found for waiting for readystate to equal 4 prevent me from returning a value at all:
function emailUsed() {
var req = $.get('scripts/email_used.php?email='+$('[name=reg_email]').val());
//Wait for req.readystate to be 4
return req.responseText == 'True';
}
But nowhere can I find something that explains how to do this and still return a value. Everything seems to use callback functions and this and that, but I can't get THIS method to return a value.
Help, please!
Doing a busy wait for req.readyState === 4 is considered bad design practice. You're tying up the UI thread and preventing the browser from updating. If the server is slow enough to respond, the user will get prompted whether to cancel further processing.
If you take a look at $.get(), it takes a completion function as one of its arguments. You should perform your success/failure logic in this function. You can do this by disabling your Submit button until you get back a success message.
You're missing the point of asynchronous methods. The main point is that there is some calculation to be done, and you don't want to tie up the current thread waiting for that calculation. Therefore, long running methods should not return a value, but instead they should provide a callback that will be passed the status of the call, without making the entire app wait.
I would suggest the following
function emailUsed (callback) {
var req = $.get('scripts/email_used.php?email='+$('[name=reg_email]').val(),
function(data) {
callback(data == 'True');
}
);
}
$('[name=reg_email]').change( function() {
emailUsed(function(isUsed){
if (isUsed) {
//Update image to be a green checkmark
} else {
//Update image to be a huge red X
}
});
}
Okay, so I appreciate that Javascript is not C# or PHP, but I keep coming back to an issue in Javascript - not with JS itself but my use of it.
I have a function:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
}
Thing is, owing to Javascript's burning desire to jump ahead in the code, the loader never appears because the 'hideLoader' function runs straight after.
How can I fix this? Or in other words, how can I make a javascript function execute in the order I write it on the page...
The problem occurs because AJAX is in its nature asynchronus. This means that the updateStatus() calls are indeed executed in order but returns immediatly and the JS interpreter reaches hideLoader() before any data is retreived from the AJAX requests.
You should perform the hideLoader() on an event where the AJAX calls are finished.
You need to think of JavaScript as event based rather than procedural if you're doing AJAX programming. You have to wait until the first call completes before executing the second. The way to do that is to bind the second call to a callback that fires when the first is finished. Without knowing more about the inner workings of your AJAX library (hopefully you're using a library) I can't tell you how to do this, but it will probably look something like this:
showLoader();
updateStatus('cron1', function() {
updateStatus('cron2', function() {
updateStatus('cron3', function() {
updateStatus('cronEmail', function() {
updateStatus('cronHourly', function() {
updateStatus('cronDaily', funciton() { hideLoader(); })
})
})
})
})
})
});
The idea is, updateStatus takes its normal argument, plus a callback function to execute when it's finished. It's a reasonably common pattern to pass a function to run onComplete into a function which provides such a hook.
Update
If you're using jQuery, you can read up on $.ajax() here: http://api.jquery.com/jQuery.ajax/
Your code probably looks something like this:
function updateStatus(arg) {
// processing
$.ajax({
data : /* something */,
url : /* something */
});
// processing
}
You can modify your functions to take a callback as their second parameter with something like this:
function updateStatus(arg, onComplete) {
$.ajax({
data : /* something */,
url : /* something */,
complete : onComplete // called when AJAX transaction finishes
});
}
I thinks all you need to do is have this in your code:
async: false,
So your Ajax call would look like this:
jQuery.ajax({
type: "GET",
url: "something.html for example",
dataType: "html",
async: false,
context: document.body,
success: function(response){
//do stuff here
},
error: function() {
alert("Sorry, The requested property could not be found.");
}
});
Obviously some of this need to change for XML, JSON etc but the async: false, is the main point here which tell the JS engine to wait until the success call have returned (or failed depending) and then carry on.
Remember there is a downside to this, and thats that the entire page becomes unresponsive until the ajax returns!!! usually within milliseconds which is not a big deals but COULD take longer.
Hope this is the right answer and it helps you :)
We have something similar in one of our projects, and we solved it by using a counter. If you increase the counter for each call to updateStatus and decrease it in the AJAX request's response function (depends on the AJAX JavaScript library you're using.)
Once the counter reaches zero, all AJAX requests are completed and you can call hideLoader().
Here's a sample:
var loadCounter = 0;
function updateStatuses(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
}
function updateStatus(what) {
loadCounter++;
//perform your AJAX call and set the response method to updateStatusCompleted()
}
function updateStatusCompleted() {
loadCounter--;
if (loadCounter <= 0)
hideLoader(); //hide the 'loader.gif' in the UI
}
This has nothing to do with the execution order of the code.
The reason that the loader image never shows, is that the UI doesn't update while your function is running. If you do changes in the UI, they don't appear until you exit the function and return control to the browser.
You can use a timeout after setting the image, giving the browser a chance to update the UI before starting rest of the code:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
// start a timeout that will start the rest of the code after the UI updates
window.setTimeout(function(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
},0);
}
There is another factor that also can make your code appear to execute out of order. If your AJAX requests are asynchronous, the function won't wait for the responses. The function that takes care of the response will run when the browser receives the response. If you want to hide the loader image after the response has been received, you would have to do that when the last response handler function runs. As the responses doesn't have to arrive in the order that you sent the requests, you would need to count how many responses you got to know when the last one comes.
As others have pointed out, you don't want to do a synchronous operation. Embrace Async, that's what the A in AJAX stands for.
I would just like to mention an excellent analogy on sync v/s async. You can read the entire post on the GWT forum, I am just including the relevant analogies.
Imagine if you will ...
You are sitting on the couch watching
TV, and knowing that you are out of
beer, you ask your spouse to please
run down to the liquor store and
fetch you some. As soon as you see
your spouse walk out the front door,
you get up off the couch and trundle
into the kitchen and open the
fridge. To your surprise, there is no
beer!
Well of course there is no beer, your
spouse is still on the trip to the
liquor store. You've gotta wait until
[s]he returns before you can expect
to have a beer.
But, you say you want it synchronous? Imagine again ...
... spouse walks out the door ... now,
the entire world around you stops, you
don't get to breath, answer the
door, or finish watching your show
while [s]he runs across town to
fetch your beer. You just get to sit
there not moving a muscle, and
turning blue until you lose
consciousness ... waking up some
indefinite time later surrounded by
EMTs and a spouse saying oh, hey, I
got your beer.
That's exactly what happens when you insist on doing a synchronous server call.
Install Firebug, then add a line like this to each of showLoader, updateStatus and hideLoader:
Console.log("event logged");
You'll see listed in the console window the calls to your function, and they will be in order. The question, is what does your "updateStatus" method do?
Presumably it starts a background task, then returns, so you will reach the call to hideLoader before any of the background tasks finish. Your Ajax library probably has an "OnComplete" or "OnFinished" callback - call the following updateStatus from there.
move the updateStatus calls to another function. make a call setTimeout with the new function as a target.
if your ajax requests are asynchronous, you should have something to track which ones have completed. each callback method can set a "completed" flag somewhere for itself, and check to see if it's the last one to do so. if it is, then have it call hideLoader.
One of the best solutions for handling all async requests is the 'Promise'.
The Promise object represents the eventual completion (or failure) of an asynchronous operation.
Example:
let myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR or an HTML5 API.
setTimeout(function(){
resolve("Success!"); // Yay! Everything went well!
}, 250);
});
myFirstPromise.then((successMessage) => {
// successMessage is whatever we passed in the resolve(...) function above.
// It doesn't have to be a string, but if it is only a succeed message, it probably will be.
console.log("Yay! " + successMessage);
});
Promise
If you have 3 async functions and expect to run in order, do as follows:
let FirstPromise = new Promise((resolve, reject) => {
FirstPromise.resolve("First!");
});
let SecondPromise = new Promise((resolve, reject) => {
});
let ThirdPromise = new Promise((resolve, reject) => {
});
FirstPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("First! ");
SecondPromise.resolve("Second!");
},
error: function() {
//handle your error
}
});
});
SecondPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Second! ");
ThirdPromise.resolve("Third!");
},
error: function() {
//handle your error
}
});
});
ThirdPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Third! ");
},
error: function() {
//handle your error
}
});
});
With this approach, you can handle all async operation as you wish.