.innerHtml text updates during script execution - javascript

I am confused as to why I cannot get text updates during a javascript/jquery/ajax execution.
I have the following code.
$("#updates").html("Collecting your team names.");
YourTeamFirst(YourTeam, TeamValue);
$("#updates").html("Collecting their team names.");
TheirTeamNext(TheirTeam, TeamValue);
$("#updates").html("Gathering ID's.");
setNames(TeamValue);
$("#updates").html("Setting Details.");
setChampRatios(TeamValue);
$("#updates").html("Setting Ratios.");
setChampKDRS(TeamValue);
$("#updates").html("Finished!");
i.e.
Example function (the only ajax that occurs is in the sub-functions, the actual function calls and text updates are in a plain JS function, no ajax on that...)
function TheirTeamNext(TheirTeam, TeamValue) {
$.ajax({
url: "/PlayerLookUp/TheirRankedData",
type: "POST",
data: "IDList=" + T[i],
dataType: "json",
async: false,
success: function (resp) {
//just boring logic
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
if (XMLHttpRequest.status == 0) {
alert("Issue with reaching Riot API server. Pleast contact an Admin.");
}
}
});
}
And the only thing that ever appears is "Finished" (after the script is done), why won't anything else appear at all? The execution takes about 10 seconds so other updates should be popping up in the innerHtml/Html. I have also tried using document.getElementById("updates").innerHTML = ..., which also shows nothing then when its done will show the "Finished" text.
Each function is either JQuery, Ajax, or javascript with a few back and forth's from my C# controller, but I feel like the text updates should still be updating my innerHtml text unless there is some script thing I am unaware of for innerHtml/Html...
ALSO: If I toss in an alert() somewhere, the most previous text update will appear. So how come only an alert interruption or end of script execution will update/post my text. I would like my users to see updated messages on going with the script.

because ajax is executed ansynchronous.
if you want text like that to appear, you need to use callbacks in your ajax functions.
update: since you are using async: false this behaviour seems really strange, but I don't know how javascript and jquery exactly handle synchronous calls in your case.
one hotfix-suggestion would be to still add callbacks to your functions. (note: this is a very messy workaround and should just help you hotfixing your problem.)
function YourTeamFirst(YourTeam, TeamValue, callback) {
/* ... */
$.ajax({
/* ... */
success: function (resp) {
/* ... */
callback();
}
});
}
then in your function calls add anonymous functions as callback-parameter. in the body of these functions always add the .html() for the next function to be executed.
$("#updates").html("Collecting your team names.");
YourTeamFirst(YourTeam, TeamValue, function () {
$("#updates").html("Collecting their team names.");
});
TheirTeamNext(TheirTeam, TeamValue, function () {
$("#updates").html("Gathering ID's.");
});
/* ... */

Related

jQuery wait for a function to complete before new fires

I have two functions in jQuery that I want to fire in a specific order. The first function is an ajax function which updates a partial view. The other one is supposed to do some styling on the partial view, once the ajax function has completed - this function takes a parameter.
ajaxFunction();
stylingFunction(params);
I have tried the following:
ajaxFunction(function() {
stylingFunction(params)
});
Also, I have tried to use a callback:
ajaxFunction(stylingfunction(params));
ajaxFunction(callback)
{
//Do update
callback()
}
None of these do however work. The styling appears shortly where after it dissapears because the partial view is getting updated. Where am I going wrong here?
Both functions are written in my "parent" view.
You can use .done() and .fail() chained to the $.ajax call ...
I created a couple callback functions with psuedo-code inside the successCallback() since you said you only need to run the styling function "sometimes". You will want to test whatever condition inside that function to determine if you want to run the styling function. Hope this helps.
(function($) {
$(function() { //document.ready
$.ajax({ cache: false,
url: "/blah/vlah/lah",
data: { somedata: somedata }
})
.done(successCallback)
.fail(failCallback);
});
function successCallback(data) {
if (someCondition) {
stylingFunction(params);
}
};
function failCallback(jqXHR, status, error) {
console.log(jqXHR);
console.log(error);
console.log(status);
};
})(jQuery);
I created another gist which handles ajax event delegation, you may want to review and incorporate anything that seems helpful to your situation.
https://gist.github.com/inceptzero/a753d020648f49da90f8
I also created this gist on github for an ajax request queue which is a bit more elegant and robust.
https://gist.github.com/inceptzero/e64756f9162ca6aeeee5
Since you are using jQuery you could const ajaxFunc = callback => $.ajax({...}).done( data => callback) Also you could use async/await. You can read more about it on MDN.

Javascript sorting out nested functions

So I have script that is for a Bingo game. I'm having a problem running one of my functions inside another function. The idea was to have my checkBingo() function be defined outside of a .click() function. There's some ajax at work, so I'm not sure if that's coming into play here too. Looks something like:
$(document).ready(function(){
function checkBingo() {
$.ajax({
url: '/check-bingo',
type: 'GET',
success: function(data){
return data;
}
}):
}
$('#div').click(function() {
// Some stuff gets done here
$.ajax({
url: '/tile',
type: 'GET',
success: function(data){
// Does some stuff with data, then needs to check if there's a bingo.
var isBingo = checkBingo();
if (isBingo == 'something') {
// Displays something specific on the page.
} else {
// Displays other things on the page.
}
}
}):
});
Where I'm getting hung up, is that isBingo is never getting assigned the returned info. I thought it might have been because the query wasn't running fast enough, so I've tried sticking the variable in a loop until it got something assigned to it and then the console told me that my checkBingo() inside the .click function wasn't defined. I'm not sure if it's just a stupid syntax error on my part or if what I'm doing isn't possible.
Can someone verify that this is indeed possible and that I've probably just got to scour it for the syntax error?
Because this line:
var isBingo = checkBingo();
...is calling an function (checkBingo) which makes an asynchronous call and does not return anything, isBingo will be undefined.
One way to approach this would be to pass a callback function to checkBingo since JavaScript allows functions to be passed around like data, and the function will be called by jQuery when the data is obtained from the server:
function checkBingo(callback) {
$.ajax({
url: '/check-bingo',
type: 'GET',
success: function(data){
callback(data);
}
// or you could just do:
// success: callback,
});
}
// ....
success: function(data){
checkBingo(function (isBingo) {
if (isBingo == 'something') {
// Displays something specific on the page.
} else {
// Displays other things on the page.
}
});
Another approach, which would allow you to continue using your synchronous style (i.e., where checkBingo could return something and you could immediately use it) even though the code is not executed synchronously is by taking advantage of the fact that the later versions of jQuery's Ajax API return a promise object which allows this style of coding:
$(document).ready(function(){
function checkBingo() {
return $.ajax({
url: '/check-bingo.txt',
type: 'GET'
});
}
$('#div').click(function() {
// Some stuff gets done here
$.ajax({
url: '/tile.txt',
type: 'GET',
success: function(data){
var checkingBingo = checkBingo();
checkingBingo.done(function (isBingo) {
if (isBingo == 'something') {
alert('a');
// Displays something specific on the page.
} else {
alert('b');
// Displays other things on the page.
}
});
}
});
});
});
Besides the need to convert a couple of your colons into semi-colons, and add the jQuery $ in front of your "#div" code, two other aspects to note:
I added the ".txt" extension to the Ajax calls in case the extension was merely hidden on your system.
The code $('#div') presumes that there is an element on your page with the ID set to "div". If you want all div elements to be clickable, you would simply need to do $('div').

Bizarre JavaScript bug, caused either by syntax, Cytoscape Web, or jQuery

I'm writing a small web app using CytoscapeWeb. It downloads an XML file containing a graph, and displays it. I've encountered an issue where it will not display the graph (showing instead a blank graph), even though the file is known to be sound. After several days of scouring my code to no avail, I began modifying the example code provided by the tutorial to reproduce the issue.
This code works:
function get_network(filename)
{
var output = "";
$.ajax({url: filename, type: "GET", dataType: "text", success: function(result) { output = result; } });
alert(filename);
alert(output);
return output;
}
And this code does not:
function get_network(filename)
{
var output = "";
$.ajax({url: filename, type: "GET", dataType: "text", success: function(result) { output = result; } });
//alert(filename);
//alert(output);
return output;
}
The only difference being that the two alert() statements are commented out. When only the first statement (alert(filename);) is removed, the alert box shows an empty string. So it would seem the blank graph is caused by an issue wherein the output variable is not properly set.
I've tested this in Firefox and Internet Explorer. It works if there is one alert statement that prints the string "ASDFSADF" rather than the output variable. Interestingly however, code that does not use the alert() statement, such as 'var junk = "ASDFSADF"', does not work.
So it seems to me that there are three possibilities:
There is some sort of syntax or logic mistake I've made, which makes the number of lines parsed significant
Cytoscape Web is causing the issue, in a manner I cannot imagine
jQuery is not calling the 'success' function
I am beginning to suspect however that this issue is beyond my expertise, and is caused by something I have not considered.
Where that syntax error could be however is beyond me. I've searched high and low. Has anyone seen something like this, or otherwise know that is happening?
Thank you very much for your assistance.
The full code:
http://pastebin.com/rvcV3LFL
The XML file:
http://pastebin.com/HCyuKQnx
The output variable is not set at the time of execution of the alert because the AJAX call is asynchronous (the A in AJAX is for asynchronous).
What ever you need to happen after the AJAX call completes will need to be passed as a callback.
So if your code is something like this:
var graph = get_network(filename);
draw(graph);
you would need to change the get_network to:
function get_network(filename,callback)
{
var output = "";
$.ajax({url: filename, type: "GET", dataType: "text", success: function(result) {
callback(result);
}
and the calling code would then be
get_network(filename,draw);
where draw is still the function from the first example
The alerts are stopping the execution thread long enough for the response to come back. If your server took 10 seconds to respond and you closed the alerts after 5 seconds, it would not work.
The jQuery ajax function doesn't take a callback function just for fun to make your code more ugly, it takes it because the execution is asynchronous and the response is only guaranteed to be available inside the callback.
Run whatever code you need to run that depends on the response, inside the success callback function.
You have already done this with window.onload = function(){} <-- only the code inside that function is guaranteed to run after window has "loaded". The code outside it is just executed straight away sequentially. Do the same with ajax.

How to avoid execution delay race condition when dynamically loading JavaScript class files with jQuery?

I have about half a dozen JS class files right now. Instead of putting in a bunch of tags, I used jQuery to load them dynamically--in order--like so:
var classes = ["A","B","C"...];
blah.load = function(callback) {
for (var i in classes) {
console.log("Loading "+blah.RootURL+"/"+classes[i]+".js");
$.ajax({
async: true,
url: blah.RootURL+"/"+classes[i]+".js",
dataType: "script",
error: function(jqxhr, status, error) {
console.log("Unable to load "+classes[i]+": "+status+", "+error);
},
success: function(data, status, xhr) {
if (window.tmpLoadCount) {
window.tmpLoadCount++;
} else {
window.tmpLoadCount = 1;
}
if (window.tmpLoadCount == classes.length) {
console.log("Initializing...");
callback();
}
}
});
}
};
Currently I'm using multiple script tags so I can keep working. The problem I had with the jQuery approach was that sometimes, when [re]loading, previous scripts didn't seem to execute (or finish executing) before the next script was loaded, even with async: true. My classes are pretty short and simple. I would see the script load via the "Loading" log, but then later see an error like "Unable to load B: A is not a constructor" (if B inherits from A), which suggests to me that A wasn't fully loaded when the B.prototype=new A(); line was executed.
I found one reference to this online, and that suggested adding a setTimeout() call to give the engine some time, and suggested that this only happened when processing stuff in jQuery's "success" callback...unfortunately I'm not sure how to ensure that everything gets loaded in order in a dynamic way without using the success callback.
Aside from many <script/> tags or minifying (which makes debugging harder) or requiring class changes, does anyone have suggestions on how to cleanly avoid this "race condition"? My current thought, which I don't really like, is to have the final line in each class file add it's class name to a global static property, and have the ajax call use an interval to check that property to determine if it should load the next file...bleh.
RequireJS is actually designed for this kind of situation. You should give serious thought to using it. I personally consider this blog post by Aaron Hardy to be a better, shorter, clearer description of what RequireJS is and how to use it than what is actually contained in the their own docs. You might take a few minutes to read through that and I think you'll find it a pretty good solution.
one thing u can do in present senario is that call the loading of next script only when the present is done. ie in the success function something like this.
function something(i){
$.ajax({
async: true,
url: blah.RootURL+"/"+classes[i]+".js",
dataType: "script",
error: function(jqxhr, status, error) {
console.log("Unable to load "+classes[i]+": "+status+", "+error);
},
success: function(data, status, xhr) {
if (window.tmpLoadCount) {
window.tmpLoadCount++;
} else {
window.tmpLoadCount = 1;
}
something(window.tmpLoadCount);
if (window.tmpLoadCount == classes.length) {
console.log("Initializing...");
callback();
}
}
});
}

jQuery: insert html with javascript, waiting for it to run

I'm using $.ajax method to pull some html code and insert it into a 'div.listbox' element, using $('div.listbox').html() method.
I'm aware that .html will insert all HTML code, and execute all javascript code found under the HTML code.
What is actually happening:
$.ajax({
async: false,
url: 'ReturnSomeDataAsJSON',
data: {some_needed_data},
dataType: 'json',
success: function(data){
$('div.listbox').html(data.body)}
})
This data.body has a javascript that will make a call to an asynchronous function that will update an element inside the HTML under data.body.
Putting a .live function on the 'div.listbox' element, in order to listen to DOMNodeInserted event, I could see that the javascript method executed by the $...html(data.body) call updated 'div.listbox' element 6 times.
As this number could change, I can't just treat this as my solution, waiting to the element to change 6 times and then do what I want.
So I'm asking if it's possible to wait untill all javascript inside that .html call is executed before continuing to other javascript methods after the $.ajax call.
The only way would be to use a callback function inside your ajax-generated javascript, so you'd have:
//(ajax generated code)
<script>
...
$('div.listbox').css("color", "blue"); //For example, let's assume this code executes asynchronously
div_changed(); //This is the callback function
</script>
Then, in your main script you should have:
$.ajax({
async: false,
url: 'ReturnSomeDataAsJSON',
data: {some_needed_data},
dataType: 'json',
success: function(data){
$('div.listbox').html(data.body)
}
})
function div_changed(){
//Here put the code you want to be executed after changes are made
}
This is the only way, note that this is asynchronous.
Hope this helps. Cheers
JavaScript is a functional programming language meaning that you almost everywhere work with functions and can also pass functions as parameters. Let's describe your scenario: In scope A (parent element) you want to do something, but just when in scope B (child element) something happens and finishes. Only scope A knows what it wants to do, and only scope B knows when it finishes its job. So what do you do? Here are some solutions:
Hardcoding your logic of scope A in scope B (spaghetti code)
Get the function back from scope B and execute it in scope A (bad idea)
Pass the logic of scope A as a function to the scope B and let B be the agent to decide when to execute that.
The best method is the third item and this is the spirit of functional programming.
Solve by adding a listener to DOMSubtreeModified event, by doing:
$.ajax({
async: false,
url: 'ReturnSomeDataAsJSON',
data: {some_needed_data},
dataType: 'json',
success: function(data){
external_data = $(data.body)
var data_html = $(data.body)[0]
var data_script = $(data.body)[1].text
listbox_container = $('form#main_form div.listbox-container:nth(0)')
//fill body
listbox_container.html(data_html);
// attach listener
listbox_container.live('DOMSubtreeModified', check_listbox)
// eval script
eval(data_script)
// this script will (some time, asynchonously) update listbox_container
// by adding some data, that I need to deal with
}
})
function check_listbox() {
listbox_internal_ container = $('form#main_form div.listbox-container:nth(1)')
if (listbox_internal_container.length >= 1) {
// do what I want (deal with new data filled),
// because internal container (that will be created
// by the $.ajax call) now exists with some data filled by the
// eval(...) of the script
};
}

Categories