I am needing to have a bit of text show up after my each() has completed all tasks within it. But it seems they are running async and the "done." displays too early. I have tried to use a delay, but it doesn't matter. And as you can see, I'm sending success and fail information to a modal for displaying which records were saved or failed. The 'done' needs to come after all $.ajax calls are complete.
$(".QueueGrid tbody > tr").each(function () {
dcn = "";
if ($(this).find(".chkOtherSelected").prop("checked")) {
claimnum = $(this).find("td:eq(2)").text();
dcn = $(this).find("td:eq(5)").text();
$.ajax({
type: "POST",
url: "Approval_Inbox.aspx/SaveNote",
data: "{dcn:'" + dcn + "',note:'" + note + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
cache: false,
timeout: 8000,
beforeSend: function () {
str = "- " + dcn + "<br />";
},
success: function (data) {
_savedSucess += str;
},
error: function () {
_savedFail += str;
},
complete: function (data) {
$("#divMassMessage").fadeIn("fast");
$("#divMassMessage").center();
$('#MainContent_savedSucess').html(_savedSucess);
$('#MainContent_savedFail').html(_savedFail);
}
});
}
});
$(".saveComplete").html("done.");
In the AJAX complete function, you may be able to check whether this is the last member of the array of elements you're parsing through, i.e. get a count of the number of elements in the $().each array and check whether the current element is equal to length of the array. If it is, write the completed message. If not, there are still more elements to loop through.
AJAX, by its very nature, is asynchronous. You can force it to be synchronous (async: false), but that will lock the browser during requests.
Alternately you can count the number of requests made, then call a function on complete every time. Count the number of completes and when it matches the number of calls, trigger your alert.
Related
I have a general ajax function which I'm calling from loads of places in my code. It's pretty standard except for some extra debugging stuff I've recently added (to try to solve this issue), with a global 'ajaxworking' variable:
rideData.myAjax = function (url, type, data, successfunc) {
var dataJson = JSON.stringify(data),
thisurl = quilkinUrlBase() + url;
if (ajaxworking.length > 0) {
console.log(thisurl + ": concurrent Ajax call with: " + ajaxworking);
}
ajaxworking = thisurl;
$.ajax({
type: type,
data: dataJson,
url: thisurl,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: true,
success: function (response) {
ajaxworking = '';
successfunc(response);
},
error: webRequestFailed
});
};
Now, there's one section of my code where a second ajax call is made depending on the result of the first:
getWebRides = function (date) {
var rideIDs = [];
var intdays = bleTime.toIntDays(date);
rideData.myAjax("GetRidesForDate", "POST", intdays, function (response) {
rides = response;
if (rides.length === 0) {
$('#ridelist').empty(); // this will also remove any handlers
qPopup.Alert("No rides found for " + bleTime.DateString(date));
return null;
}
$.each(rides, function (index) {
rideIDs.push(rides[index].rideID);
});
GetParticipants(rideIDs);
});
},
'GetParticipants' (which also calls 'myAjax') works fine - most of the time. But in another part of my code, 'GetWebRides' is itself called directly after another ajax call - i.e. there are 3 calls, each successive one depending on the previous. The 'top-level' call is as follows:
rideData.myAjax("SaveRide", "POST", ride, function (response) {
// if successful, response should be just a new ID
if (response.length < 5) {
// document re-arrangement code snipped here for brevity
getWebRides(date);
}
else {
qPopup.Alert(response);
}
});
so, only when there are three successive calls like this, I'm getting the 'concurrent' catch in the third one:
GetParticipants: concurrent call with GetRidesForDate
and (if allowed to proceed) this causes a nasty probem at the server with datareaders already being open. But why is this only occurring when GetParticipants is called as the third in the chain?
I see, after some research. that there are now other ways of arranging async calls, e.g. using 'Promises', but I'd like to understand what's going on here.
Solved this.
Part of the 'document re-arrangement code' that I had commented out for this post, was in fact calling another Ajax call indirectly (very indirectly, hence it took a long time to find).
Based on the flag 'isConformpage' we are adding some log in the database.
The log code is triggered via an AJAX call.
The flag condition is not being passed even though the value is 'true'.
It works if I add an alert() before the if condition.
PS: WriteLog1 method return in VB.net. Mentioning here although it doesn't have any impact on the question.
try {
setTimeout(function () {
//your code to be executed after 5 second
if (isconformpage) {
var split = document.getElementById("hdnconfirmpage").value.split("#");
transaction = split[0];
transaction = jQuery.parseJSON(transaction);
user = jQuery.parseJSON(split[1]);
component = jQuery.parseJSON(split[2]);
//Tagging log start
var pathurl = "/" + loc[1] + "/thankyou.aspx/WriteLog1";
$.ajax({
type: "POST",
url: pathurl,
data: '{Message: "' + "-" + '"}',
contentType: "application/json; charset=utf-8",
dataType: "json",
// success: OnSuccess1,
failure: function (response) {}
});
// Tagging log end
}
}, 5000);
}
You are "Missing catch or finally after try"
Check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
I need to return value from ajax but it filled 0 every time and didn't wait for ajax process finished
var itemId=0; as global value
getitemIDbyProductID(productId,getitemIDbyProductID_success);
alert(itemID + "itemgeted")
I did this
function getitemIDbyProductID(productId, callback) {
$.ajax({
type: "POST",
url: "Cot.aspx/getitemIDbyProductID",
data: JSON.stringify({ productId: productId }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) {
var value = 0;
value = JSON.parse(result.d);
itemID=callback(value)
callback(value);
},
error: function (msg) { }
});
}
function getitemIDbyProductID_success(total_percentage) {
alert(total_percentage +"fds"+itemID);
}
but it didn't wait the ajax finished and gives me the itemId = undefiend
You're successfully setting the value here:
function getitemIDbyProductID_success(total_percentage) {
itemID = total_percentage;
alert(total_percentage +"fds"+itemID);
}
But then, in the code which calls this, you're successfully setting it again:
itemID=callback(value)
Since your getitemIDbyProductID_success doesn't return anything, the return value is undefined. So basically you're unsetting itemID immediately after setting it.
Just invoke the callback, don't use its (non-extant) return value:
callback(value);
Additionally, this isn't going to do what you think:
getitemIDbyProductID(productId,getitemIDbyProductID_success);
alert(itemID + "itemgeted");
Because getitemIDbyProductID performs an asynchronous operation. Even once the above errors are corrected, this error still remains. And this one is a duplicate of a very popular question (with answers far better than I can provide) here.
You can do something like this:
getitemIDbyProductID(productId,function(val){
itemID = val;
alert(itemID + "itemgeted");
});
Basically, you have to wait before the itemID gets assigned right value.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
i have js code which returns rows of data for my table. On clicking each of those rows they expand and plan is to show child data of that row by calling another ajax request.
My code for child data looks like below:
function format ( d ) {
var results;
$.ajax({ url: 'http://127.0.0.1:7101/MUDRESTService/rest/v1/feedbacks/' +
d.FeedbackId + '/child/MudFeedbackDetailsVO?onlyData=true',
type: 'get',
dataType: 'json',
success: function(output) {
console.log(output.items[0].CriticalPath) ;
results = output.items[0];
}
});
return results.CriticalPath;
}
The problem probably is that method doesn't finish by the time value of results.CriticalPath is returned. I can see value in chrome js console so there is no problem with the data part.
So how should i make it return the value once the response is ready
When writing asynchronous code, you need to start working with callbacks rather than return values.
Your functions, like format here, only initiates an action. Updates to the UI are initiated by the callback.
Instead of this logic:
function doSomething() {
var result = format(d);
doSomethingWithResult(result);
}
You need to adapt to this:
function doSomething() {
var result = format(d, doSomethingWithResult);
}
function format( d, callback ) {
$.ajax(..., {
success : function(output) {
var results = output.items[0];
callback(results); // this is where we call doSomethingWithResult
}
});
}
Now I'm no exert at this, but hopefully you'll find something from the code-example that you can use.
I'm binding each row on the .done()-function, which calls another api. I hope this helps.
JS-FIDDLE
(function(){
//getJSON example
var jqxhr = $.getJSON( "https://api.myjson.com/bins/2emll", function(data) {
for (key in data) {
$("#list").append("<li class='" + key + "'>" + data[key] + "</li>");
}
}).done(function( data ) {
$("#list li").on("click", function(e){
var target = e.target.className;
//ajax example
$.ajax({
url: 'https://api.myjson.com/bins/309x5',
type: 'get',
data: target,
dataType: 'json',
success: function(data) {
var title = $("." + target).text();
$("." + target).html(title + '<ul id="ul-' + target + '"></ul>');
}
}).done(function(data){
for (key in data) {
$("#ul-" + target).append("<li class='" + key + "'>" + data[key] + "</li>");
}
});
});
});
})();
you can try setting async option to false
like this
function format ( d )
{
var results;
$.ajax({
url: 'http://127.0.0.1:7101/MUDRESTService/rest/v1/feedbacks/' + d.FeedbackId + '/child/MudFeedbackDetailsVO?onlyData=true',
type: 'get',
dataType: 'json',
async:false,
success: function(output)
{
console.log(output.items[0].CriticalPath) ;
results = output.items[0];
}
});
return results.CriticalPath;
}
NOTE :
but it will make your ajax as synchronous and it may be possible that your browser will be unresponsive for the request so there are some points to be noted before using it
By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() or the deprecated jqXHR.success().
The first letter in Ajax stands for "asynchronous," meaning that the operation occurs in parallel and the order of completion is not guaranteed. The async option to $.ajax() defaults to true, indicating that code execution can continue after the request is made. Setting this option to false (and thus making the call no longer asynchronous) is strongly discouraged, as it can cause the browser to become unresponsive.
fore more information you can read here
I'm looping through an array, and during each iteration of the loop, I'm calling a url through ajax. I'd like to also update an .innerHTML such that it displays to keep the user informed as to which iteration of the loop is being processed. However, .innerHTML only displays the update when the script completes.
How can I make this notification display during my loop?
I'm also using the query ajax setting 'async: false'. I don't want to hammer my server with processing all of the ajax requests at once, as they are encoding video files which is CPU intensive. I don't really want to lock the browser up waiting for synchronous requests to complete either.
Is there a better way to do this?
My ultimate goal is to sequentially execute my combine.php script for each set of videos, while displaying a current status indicator to the user, and while not locking the browser up in the process. Your help is appreciated!
Code snippet here:
// process the loop of videos to be combined
var status = document.getElementById('currentStatus');
for (i=0; i< count; i++) {
// change the display
var fields = videos[i].split(":", 2);
current = i +1;
currentStatus.innerHTML = "<b>Multi-part Videos:</b> <h3 class='status'>Currently Updating Bout #" + fields[1] + " (" + current + " of " + count + " videos)</h3>";
// run the combine
var dataString = 'videoId='+ fields[0];
$.ajax({
type: "POST",
url: "combine.php",
data: dataString,
success: function(txt) {
//deselect the checkbox
document.combine.video[selected[i]].checked = false;
},
async: false
});
async: false will hang the entire browser until the ajax request completes. That is why you don't see the page update on each loop iteration.
Synchronous ajax requests typically make for terrible UX (do you like the page to freeze inexplicably?) and there is almost always a better way to do it. Since you're using jQuery, the Deferred Object API makes this easy.
As others have alluded, your problem is caused because JavaScript is single threaded - while the single JS thread is waiting for your ajax request to return, it's not allowed to update the UI.
You can get around this by changing the request to async, and using the callback to trigger the request for the next object:
// trigger the loop of videos to be combined
var status = document.getElementById('currentStatus');
processVideo( 0 );
function processVideo( index ) {
var fields = videos[index].split(":", 2);
currentStatus.innerHTML = "<b>Multi-part Videos:</b> <h3 class='status'>Currently Updating Bout #" + fields[1] + " (" + current + " of " + count + " videos)</h3>";
// run the combine
var dataString = 'videoId='+ fields[0];
$.ajax({
type: "POST",
url: "combine.php",
data: dataString,
success: function() {
processResponse( index);
},
async: true
});
}
function processResponse( index ) {
// this method is called each time the ajax request finishes
if (index++ < count) {
//deselect the checkbox
document.combine.video[selected[index]].checked = false;
processVideo( index );
}
}
If you want to update one by one while async is set to true, the next request can be put in the success callback function. The update status code should be inside that function too.
function ajaxRequest(i){
// other processing
.............
$.ajax({
type: "POST",
url: "combine.php",
data: dataString,
success: function(txt) {
//deselect the checkbox
document.combine.video[selected[i]].checked = false;
// update status
currentStatus.innerHTML = .....
// make next request
if(i<lastOne){
ajaxRequest(i+1);
}
},
async: true
});
}