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.
Related
This one will require some setup for you to understand what I am trying to do. It involves both templating and asynchronous calls. I am aware of the intricacies and nuances of async calls.
I have a piece of javascript, in a .js file. The script contains some "tags" that need to be replaced with actual variables, which are different on each use. You will notice the "{{tags}}" embedded in the script. You will also notice that the script contains an ajax call to a C# Generic Handler. :
// this is template code from LoadFlights.js, called from LoadFlights() in main.js...
$.ajax({
type: "POST",
url: "js/handlers/LoadFlights.ashx",
dataType: "text",
cache: false,
data: {
DateStart: "{{DATESTART}}",
DateEnd: "{{DATEEND}}",
Specific: JSON.stringify({DAY: "{{DAY}}", DEP: "{{DEP}}", CARRIER: "{{CARRIER}}", FLT: "{{FLT}}", LEGCD: "{{LEGCD}}"})
},
success: function (result) {
callback_LoadFlights(result);
},
error: function (result) {
alert(result.responseText);
return false;
}
});
function callback_LoadFlights(result) {
alert(result);
}
// end
I get the script with a jquery .get() call, and in the .done() callback, I attempt to assign the retrieved script code to a variable.
function runScript(source, parameters) {
if (URLExists(source)) {
var getScript = $.get({
url: source,
dataType: "script"
})
.done(function (scriptCode) {
var code = scriptCode;
// replace any passed parameters...
for (var p in parameters) {
code = code.replace("{{" + p + "}}", parameters[p]);
}
// remove any unused parameter placeholders...
while (code.indexOf("{{") >= 0) {
code = code.substr(0, code.indexOf("{{")) + code.substr(code.indexOf("}}") + 2);
}
var s = document.createElement('script');
s.type = "text/javascript";
s.text = code;
document.body.appendChild(s);
})
.fail(function () {
alert("Failed to retrieve script: " + source);
})
}
(I omitted the else for brevity sake.)
What happens is that on this line:
var code = scriptCode;
The code immediately executes, and the Generic Handler call fires, and immediately fails with "invalid date format" (the first line that attempts to use DateStart) because DateStart still equals "{{DATESTART}}". None of the code that replaces the tags executes.
Even if I set a breakpoint on that line and attempt to step INTO it to see what might be happening, it still immediately fires the generic handler call.
In the debugger, I typeof'd both code and scriptCode in the Immediate Window, and both return "string".
I'm tempted to believe that a JavaScript error of some sort is occurring, immediately killing the JavaScript code block and stopping it's execution.
But HOW is the Generic Handler being fired then? By all appearances, it seems as though the javascript retrieved by the .get().done() is being executed by simply assigning it to another variable. Is this even possible? Can anyone see what is wrong here?
Take a look at the documentation for jQuery.ajax(): http://api.jquery.com/jquery.ajax/
When the dataType is script:
"script": Evaluates the response as JavaScript and returns it as plain text.
So jQuery is evaluating your javascript before you've had a chance to parse it. Then it gives you the text of the script, but by this point it's too late. Try changing the datatype to 'text' then parse it.
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.");
});
/* ... */
I've written a function which makes an asynchronous request using jQuery.
var Site = {
asyncRequest : function(url, containerId) {
$.ajax({
url : url,
onSuccess: function(data){
$(containerId).html(data);
}
});
}
}
Syntax might be slightly wrong as I'm using notepad, but hopefully you get the idea.
I call the function:
Site.asyncRequest('someurl', container1);
Site.asyncRequest('someurl', container2);
Both requests get sent and processed by the server. Two responses get sent back, which is what I expect. However, I would expect container1 and container2 to contain responses from both requests.
The problem, is that only the last response gets displayed and I can't figure out why. I don't know how the jQuery ajax keeps a track of requests/responses, so maybe this is a problem.
Say I make 5 or 10 requests, how does jQuery ajax know which response is for which request and where does it keep a track of it?
Thank you
This appears to be a Javascript scoping issue. Try the following:
var Site = {
asyncRequest: function(url, containerId) {
(function(theContainer) {
$.ajax({
url: url,
onSuccess: function(data) {
$(theContainer).html(data);
}
});
})(containerId);
}
};
This creates a separate scope for each function call, so the actual value pointed to by "theContainer" is different for each onSuccess anonymous function.
What is happening here is a single closure is getting created, due to the way that function is declared. See "A more advanced example" here: http://skilldrick.co.uk/2010/11/a-brief-introduction-to-closures/
Basically, the containerId is being shared among all instances of that onSuccess anonymous function. I haven't tested this, but I believe if you defined your asyncRequest function outside of Site, this would work.
As far as a more elegant solution to this problem, perhaps someone else will answer better.
I'm sure the solution is staring me right in the eyes, but I just cannot see it. I am trying to load an object from an outside file source. I've tried it several which ways using jQuery's built in methods, but keep returning undefined. Is my issue the scope? I need partnerData right where it is because of other dependent methods in my script. I don't want to operate the rest of my site's functions from within the $.get callback. Any help is greatly appreciated, here's the code:
$(function() {
var partnerData;
$.get('data/partners.json', function(file) {
partnerData = $.parseJSON(file);
});
console.log(partnerData); /* returns undefined instead of object */
});
EDIT:
Thanks for all the feedback everyone. This is the solution I went with:
var partnerData;
$.ajax({
url: 'data/partners.json',
dataType: 'json',
async: false,
success: function(data) {
partnerData = data;
}
});
The reason why you're seeing undefined is because ajax requests are asynchronous by default. This means your get method gets invoked and the code flow moves down to the next statement while the request executes in the background. Your callback function is later invoked when the request completes.
Using callback functions is a common pattern used in situations like this. But you seem to be saying you don't want to do or can't do that. In that case, you could use async: false which would force the request to be synchronous. Keep in mind however, that your code will be blocked on the request and if it's a long-lived request, the user experience will degrade as the browser will lock up.
P.S. You shouldn't need to parseJSON - if response has the correct mime-type set, jQuery will intelligently guess the type and parse the JSON automatically. And in case the server isn't sending back the correct mime-type, you can also explicitly tell jQuery what the expected return data type is; see the dataType argument to $.get() .
One way you might modify your code, to force synchronous requests:
$.ajax({
type: 'GET',
url: 'data/partners.json',
success: function(file){
partnerData = $.parseJSON(file);
//ideally you would perform a callback here
//and keep your requests asynchronous
},
dataType: 'json',
async: false
});
function is proccessed to the end event when ajax is still being proccessed. insert it into callback function
$(function() {
var partnerData;
$.get('data/partners.json', function(file) {
partnerData = $.parseJSON(file);
console.log(partnerData);
});
});
I would say that your problem is the same of the one that I just solved, if $.get is AJAX! and it is setting a variable, to read that variable outside the callback you need to wait the response! So you have to set async=false!
console.log in synchronous and get is async.
try:
$(function() {
var partnerData;
$.get('data/partners.json', function(file) {
partnerData = $.parseJSON(file);
test();
});
function test(){
console.log(partnerData);
}
});
I have a list of URLs and need to load each page, one after another.
This is my main function that i have in my Mind.
mainFunction() {
loop { // Loop through URL list
oPage = func1(URL); //Get page contents
aResult = func2(oPage); //Analyse the contents
func3(aResult); //Do current page modifications
}
}
func1 uses GM_xmlhttprequest, which is asynchronous, so oPage results in 'underfined' as function ends BEFORE the contents of a page could be retrieved.
func2 also uses GM_xmlhttprequest, so even no matter if oPage was undefined, aResult will be undefined too.
Any ideas on how to make all of this work?
func1 func2 and func3 should be reusable throughout the script, each of these functions may be used independently or together in different parts of script.
Is there any reason why you need to use Greasemonkey specific functionality? Are you doing cross site requests or something that specifically requires it? Looking at the Wiki for Greasemonkey, I can't find the option to set asynchronous to false.
Your easiest option is to include JQuery with your Greasemonkey script and use JQuerys AJAX functionality. Ofcourse, this can be done without JQuery, however, cross browser incompatibility in this area is quite the pain to handle manually.
Using JQuery, your code would look something like this:
function func1(url) {
var result;
$.ajax({
type: "GET",
url: url,
async: false,
success: function(data){
result = data;
}
});
return result;
}
and you would declare your variable oPage like this:
var oPage = func1(url);
The rest I think you can figure out yourself, good luck.
Normally you would put the calls inside of the xmlhttprequest's response handler, such that it returns immediately, and when it does get that page it then executes the required code.
If you really need to make them happen in a specific order, you can make the return for the first call the second, etc.
var urls = [];
(function recursive(list)
{
if (list[0]) // the list is not empty
GM_xmlhttpRequest({ // that would be "func1"
"url" : list[0], // first url in the list
"onload" : function(xhr)
{
var oPage = xhr.responseText, // page contents
aResult = func2(oPage); // analyse the contents
func3(aResult); // do current page modifications
list.shift(); // remove the first link of the list
recursive(list); // go to the next url in the list
}
});
else
alert("end of list");
})(urls);
haven't tested it but you got the idea