Accessing variable outside on load function - javascript

Hi I am working Android application development using titanium studio.I have developed small application.my problem is that I can not access variable which is define inside the xhr.on load.I used following code:
xhr.onload = function()
{
var json = this.responseText;
var to_array = JSON.parse(json);
var to_count = to_array.length;
};
I want to access to_count and to_array outside onload function and pass it to another child window.For that I used following code:
var feedWin = Titanium.UI.createWindow({
url:'home/feed.js'
});//alert(to_count);
feedwin.to_array = to_array;
feedwin.to_count = to_count;

The XHR client is asychronous by default, which means that code will continue to execute while the XHR is running. If you have code that is dependent on your XHR being finished, then you will need to either call that code from within the onload function, or force the XHR to be synchronous by adding "false" as a third parameter to xhr.send() (I've found the first option to be the more reliable one, and more in line with what Titanium expects/feels is best practice, just FYI).

The best way to accomplish this is to initialize your feedWin in the onload. So, one of the following two snippets should work:
xhr.onload = function()
{
var json = this.responseText,
feedWin = Titanium.UI.createWindow({
url:'home/feed.js'
});//alert(to_count);
feedWin.to_array = JSON.parse(json);
feedWinto_count = to_array.length;
};
or
var feedWin = Titanium.UI.createWindow({
url:'home/feed.js'
});
xhr.onload = function()
{
var json = this.responseText,
feedWin.to_array = JSON.parse(json);
feedWinto_count = to_array.length;
};
I'm not familiar with Titanium, so I don't know particulars, but that is my best guess.

I am not very familiar with Titanium, but wrt to scope of declaration, I think this is what you need to do to use them outside the function.
var to_array;
var to_count;
xhr.onload = function()
{
var json = this.responseText;
to_array = JSON.parse(json);
to_count = to_array.length;
};

Related

How can I use an ajax request to fire Webservice calls in Javascript

I am currently working on some javascript that can be included in the header of surveys that use TrueSample, and will dynamically generate and fire Webservice calls for the survey. One of the requirements of Truesample is that after every page, it is sent the amount of time spend on that page, as well as some other arbitrary information generated in the beginning of the survey. I am trying to automate the every page web service call, so that I don't have to have hundreds of web services in every survey.
I am pretty far along, and have found some cool tricks to make this all work, but I am struggling with firing the webservice using javascript.
Here is what I have so far:
Qualtrics.SurveyEngine.addOnload(function()
{
var pageStart = new Date();
var beginning = pageStart.getTime();
// Necessary Variables
var account-id = parseInt("${e://Field/account-id}");
var passcode = parseInt("${e://Field/passcode}");
var survey-country = parseInt("${e://Field/survey-country}");
var end-client-id = parseInt("${e://Field/end-client-id}");
var page-exposure-duration;
var page-id = parseInt("${e://Field/pageID}");
var platform-id = parseInt("${e://Field/platform-id}");
var respondent-id = parseInt("${e://Field/respondent-id}");
var response-id = parseInt("${e://Field/response-id}");
var source-id = parseInt("${e://Field/source-id}");
var survey-id = parseInt("${e://Field/survey-id}");
var api-version = parseInt("${e://Field/api-version}");
//End Variables
var that = this;
that.hideNextButton();
var para = document.createElement("footnote");
var test = document.getElementById("Buttons");
var node = document.createElement('input');
var next = document.getElementById("NextButton");
node.id = "tsButton";
node.type = "button";
node.name = "tsButton";
node.value = " >> ";
node.onclick = function trueSample(){
var pageEnd = new Date();
var end = pageEnd.getTime();
var time = end - beginning;
window.alert(pageID + ", time spent on page = " + time);
Qualtrics.SurveyEngine.setEmbeddedData("pageID", pageID + 1);
new Ajax.Request('webserviceURL', {
parameters: {
account-id: account-id,
passcode: passcode,
survey-country: surveycountry,
end-client-id: end-client-id,
page-exposure-duration: time,
page-id: page-id,
platform-id: platform-id,
respondent-id: respondent-id,
response-id: response-id,
source-id: source-id,
survey-id: survey-id,
api-version: api-version}
});
that.clickNextButton();
};
para.appendChild(node);
test.insertBefore(para, next);
});
Does anyone have experience with firing webservice calls out of Javascript? And if so, do you have any ideas on how to finalize the ajax request and make it work? Or is there another(potentially better) method that I could use for these calls that would work? I understand that there is information on this on Stack Overflow, but I am having a hard time understanding how specific use cases apply to mine.
Also, please note that, while I would love to use JQuery, I am limited to vanilla Javascript, and Prototype.JS.
Using Traditional javascript XmlHttpRequest you can make an AJAX call. For a Webservice, we need couple of HTTP Headers. Like: SOAPAction, Content-Type, Accept. The values for these headers MUST be like below:
SOAPAction:""
Content-Type:text/xml
Accept:text/xml
So, additionally, your code should look something like this for making an AJAX call to the Webservice:
//Get XML Request Object
var request = new XMLHttpRequest();
// Define the URL
var url="http://your.end.point.url?wsdl";
//Define HTTP Method. Always POST for a Webservice
request.open("POST", url, true); // Remember that all the Webservice calls should be POST
//Setting Request Headers
request.setRequestHeader("SOAPAction", "\"\"");//Not sure of the escape sequence. The value should be "".
request.setRequestHeader("Accept","text/xml");
request.setRequestHeader("Content-Type","text/xml");
//Make your AJAX call
request.send(soap); // where soap is you SOAP Request Payload.
Parsing the response:
request.onreadystatechange=stateChanged;
function stateChanged()
{
if (request.status==200)
{
// Success. Parse the SOAP Response
}
if(request.status==500)
{
//Failure. Handle the SOAP Fault
}
}

XMLHttpRequest on load in saveback to calling object

I'm trying to load in many json files for a HTML5 game that will serve as sprite sheets. Previously I've did this synchronously but my new goal is to do this asynchronously.
I have run into a problem though where I'm trying to saving back to the calling object. This is so the information loaded can be used later and so a flag (loaded) can be set so the system knows when a resource has been loaded. Below is my XMLHttpRequest code. I have substituted "spritesheet" for what ever the call should be to save back to the parent.
function SpriteSheet(filename)
{
var tmpFileName = "json/" + filename;
this.loaded = false;
var xhr = new XMLHttpRequest();
xhr.open("GET",tmpFileName,true);
xhr.onload = function(event){
var parsed = JSON.parse(this.responseText);
"spritesheet".img=new Image();
"spritesheet".img.src = "imgs/" + parsed["imgLoc"];
"spritesheet".animations = parsed["animations"];
"spritesheet".sprites = parsed["sprites"];
"spritesheet".loaded = true;
};
xhr.send();
}
Can somebody inform me how I can save back to the the parent or if this is completely the wrong approach can they point me in the direction of a solution.
I found that by creating a var in the 'class' that is a reference to the object and using it in the onload function works, for example:
function SpriteSheet(filename)
{
var tmpFileName = "json/" + filename;
this.loaded = false;
var caller = this;
var xhr = new XMLHttpRequest();
xhr.open("GET",tmpFileName,true);
xhr.onload = function(event){
var parsed = JSON.parse(this.responseText);
caller.img=new Image();
caller.img.src = "imgs/" + parsed["imgLoc"];
caller.animations = parsed["animations"];
caller.sprites = parsed["sprites"];
caller.loaded = true;
};
xhr.send();
}

making parallel ajax requests

i have javascript code that does these things in a loop
create a div element,append it to the dom and get its reference
pass this reference to a function that makes an ajax post request
set the response of the ajax request to the innerHTML of the passed element reference
here is the code
window.onload = function () {
var categories = document.getElementById('categories').children;
for (i = 0; i < categories.length; i++) {
var link = categories[i].children[1].children[0].attributes['href'].nodeValue;
var div = document.createElement('div');
div.className = "books";
div.style.display = "none";
categories[i].appendChild(div);
getLinks(link, div);
}
}
function getLinks(url, div) {
xhr = new XMLHttpRequest();
xhr.open('POST', 'ebook_catg.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
url = encodeURIComponent(url)
var post = "url=" + url;
xhr.node=div; //in response to Marc B's suggestion
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
xhr.node.innerHTML = xhr.responseText;
xhr.node.style.display = "block";
}
}
xhr.send(post);
}
now when i check this in firebug i can see that the div element is created and appended to the categories element and its display is set to hidden. also the ajax post requests are being sent and the response is being received as expected. But the innerHTML property of div is not set and neither its display is set to block.
This means that the function getLinks loses the div reference.
when i type console.log(div) in the firefox console it says ReferenceError: div is not defined.
can somebody explain whats going on here?
in response to Franks's comment i changed readystate to readyState and i am able to attach the response of the last ajax request to the dom. so that makes it obvious that the div reference is being lost.
Thats because you are using a public (global) variable div that keeps getting overwritten.
Try this in your for loop:
for (i = 0; i < categories.length; i++) {
var link = categories[i].children[1].children[0].attributes['href'].nodeValue;
var div = document.createElement('div'); //USE var!
div.className = "books";
div.style.display = "none";
categories[i].appendChild(div);
getLinks(link, div);
}
Remember that the response handlers innards aren't "fixated" when the callback is defined, so the 'current' value of the div var doesn't get embedded into the function's definition. It'll only be resolved when the function actually executes, by which time it might have been set to some completely other div, or been reset to null as the parent function's scope has been destroyed.
You could store the div value as a data attribute on the xhr object, which you can then retrieve from within the callback:
xhr.data('thediv', div);
xhr.onreadystatechange = function () {
if (xhr.readystate == 4) {
div = xhr.data('thediv');
etc....
Ok, you've got a few globals going on that you don't want. Rule of thumb: unless you need to access a variable outside of a function, place var in front of it. Otherwise you'll have data clobbering itself all over the place:
// changed the name to `d` because div seems to already be a global var.
function getLinks(url, d) {
// make xhr a local variable so it won't get re-written.
var request = new XMLHttpRequest();
request.open('POST', 'ebook_catg.php', true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
url = encodeURIComponent(url)
var post = "url=" + url;
request.onreadystatechange = function () {
// when the request was global, this would be false until the last
// request completed
if (request.readyState == 4) {
// since d only exists as a parameter to getLinks, this should
// already be bound when the onreadystatechange is created.
d.innerHTML = request.responseText;
d.style.display = "block";
}
}
request.send(post);
}
So, why did I just do such strange, strange things? Well, it looks like div was being assigned as a global variable and while JS should always look to function parameter name for binding, we want to eliminate all possible problems. So I changed the name of that variable. Then I set xhr to reflect a local variable with the var keyword. I also changed the name to request. Once again, it shouldn't matter -- var means that the variable will be bound to that scope, but the change is harmless and since I don't know what else you have, I decided to remove ambiguities. If it does not help JS, it will at least help the reader.
NOTE:
The important part of the above answer is var in front of request.
here i am answering my question.The following code works,i mean the response from each post is appended to the corresponding div element.
var xhr=new Array();
window.onload=function() {
var categories=document.getElementById('categories').children;
for(i=0;i<categories.length;i++)
{
var link=categories[i].children[1].children[0].attributes['href'].nodeValue;
var div=document.createElement('div');
div.className="books";
div.style.display="none";
categories[i].appendChild(div);
getLinks(link,div,i);
}
}
function getLinks(url,div,i)
{
xhr[i]=new XMLHttpRequest();
xhr[i].open('POST','ebook_catg.php',true);
xhr[i].setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
url=encodeURIComponent(url)
var post="url="+url;
xhr[i].node=div;
xhr[i].onreadystatechange=function() {
if(xhr[i].readyState==4)
{
xhr[i].node.innerHTML=xhr[i].responseText;
xhr[i].node.style.display="block";
}
}
xhr[i].send(post);
}
i am not marking it as accepted because i still dont understand why i need to use an array of xhr since a local xhr object should be enough because each time the onreadystate function executes it has the reference of the xhr object. Now since javascript functions are also objects therefore every instance of onreadystate function should have its own reference of xhr object and therefore i shouldnt need to create an array of xhrs.
please correct me if i am wrong here

Browser crashes after 10-15 mins

In my app I'm displaying 10 charts (charts are from dygraphs.) to monitor data. For displaying charts I'm getting data from my sever by sending ajax request to 4 servlets on every 5 seconds. After 10-15 mins (don't know exact time.) my browser crashes saying "aw!! snap." What could be the reason? Is it javascript that is causing it? or is it because I'm sending request every 5 seconds?
Browser tested: Firefox and Chorme.
Note:- When I refresh the browser after crash it again works fine for 10-15 mins.
JS code:
var i=0;
var loc = new String();
var conn = new String();
var heapUsage = new String();
var cpuUsage = new String();
var thrdCnt = new String();
var heapUsageConsole = new String();
var cpuUsageConsole = new String();
var thrdCntConsole = new String();
var user = new String();
var MemTotal = new String();
function jubking(){
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = "MonitorDBServlet";
xmlhttp.open("POST", url, false);
xmlhttp.send(null);
var str = xmlhttp.responseText;
var strArr = str.split(",");
url = "MonitorTomcatServlet";
xmlhttp.open("POST", url, false);
xmlhttp.send(null);
var appstr = xmlhttp.responseText;
var appArr = appstr.split(",");
url = "MonitorConsoleTomcatServlet";
xmlhttp.open("POST", url, false);
xmlhttp.send(null);
var appstrConsole = xmlhttp.responseText;
var appArrConsole = appstrConsole.split(",");
url = "CpuMemoryServlet";
xmlhttp.open("POST", url, false);
xmlhttp.send(null);
var statesStr = xmlhttp.responseText;
var states = statesStr.split(",");
if(i>30){
loc = loc.substring(loc.indexOf("\n")+1);
loc += i+","+strArr[0]+","+strArr[1]+"\n";
//--- Do same thing all other var
} else {
loc += i+","+strArr[0]+","+strArr[1]+"\n";
//--- Do same thing all other var
}
document.getElementById("dbSize").innerHTML = strArr[3];
document.getElementById("HeapMemoryUsageMax").innerHTML = appArr[1];
document.getElementById("HeapMemoryUsageMaxConsole").innerHTML = appArrConsole[1];
g = new Dygraph(document.getElementById("dbLocks"),
",locksheld,lockswait\n"+loc+"");
g = new Dygraph(document.getElementById("activeConnection"),
",Connections\n"+conn+"");
g = new Dygraph(document.getElementById("example2"),
",heapUsage\n"+heapUsage+"");
g = new Dygraph(document.getElementById("example3"),
",cpuUsage\n"+cpuUsage+"");
g = new Dygraph(document.getElementById("example4"),
",thread,peakThread\n"+thrdCnt+"");
g = new Dygraph(document.getElementById("example6"),
",heapUsage\n"+heapUsageConsole+"");
g = new Dygraph(document.getElementById("example7"),
",\n"+cpuUsageConsole+"");
g = new Dygraph(document.getElementById("example8"),
",thread,peakThread\n"+thrdCntConsole+"");
g = new Dygraph(document.getElementById("cpuStates"),
",user,system,nice,idle\n"+user+"");
g = new Dygraph(document.getElementById("memStates"),
",MT,MF,B,C,ST,SF\n"+MemTotal+"");
i = i + 1;
setTimeout("jubking()", 5000);
}
You can use about:crashes in FF to view the specific reason for your crash. As mentioned by others, you could be leaking memory if you're caching off data (assigning it to a variable) returned by your AJAX call and not clearing it when the next call is made.
Edit:
Just saw your comment - 1,923,481 K is definitely too much - you're leaking data somewhere. What OS are you running? If you run FF from console in *nix, you usually get some form of a dump into console when something's going wrong (not sure about Windows).
You could possibly try decreasing your poll intervals to once every few seconds and step through the script using Firebug or Chrome's debugger to see what's happening. Worst case, start commenting things out until you figure out exactly what is making your app crash. And then, figure out a way to fix it :)
I suspect that your dygraphs usage is, as you note in your comments, the source of your trouble. It looks like you're binding new graphs over and over again when you only want to update the data, using a moving window for the data would also help. Try reworking your updater to work like this pseudo-JavaScript:
var graphs = {
dbLocks: {
graph: new DyGraph(/* ... */),
data: [ ]
},
activeConnection: {
graph: new DyGraph(/* ... */),
data: [ ]
},
// etc.
};
var DATA_WINDOW_SIZE = 1000; // Or whatever works for you.
function update(which, new_data) {
var g = graphs[which];
g.data.push(new_data);
if(g.data.length > DATA_WINDOW_SIZE)
g.data.shift();
g.graph.updateOptions({ file: g.data });
}
function jubking() {
// Launch all your AJAX calls and bind a callback to each
// one. The success callback would call the update() function
// above to update the graph and manage the data window.
// Wait for all the above asynchronous AJAX calls to finish and
// then restart the timer for the next round.
setTimeout(jubking, 5000);
}
The basic idea is to use window on your data with a reasonable maximum width so that the data doesn't grow to chew up all your memory. As you add a new data point at the end of your data cache, you drop old ones off the other end once you hit your maximum comfortable size.
You can find some techniques for waiting for several asynchronous AJAX calls to finish over here: How to confirm when more than one AJAX call has completed? (disclosure: yes, that's one of my other answers).
The answer above advocates re-using your Dygraph object and calling g.updateOptions({file:...}) to reduce memory usage. This is a great way to do it.
The other way is to call g.destroy() before you redefine the Dygraph object. This will make dygraphs clear out all of its internal arrays and DOM references. Example:
g = new Dygraph(...);
g.destroy();
g = new Dygraph(...);
Read more here: http://blog.dygraphs.com/2012/01/preventing-dygraphs-memory-leaks.html

Passing result of xhr.onload

This should be a very simple concept, but I just don't understand. In a Titanium app I have an array of data used by several windows, my xhr result needs to be passed to the top level of my app's namespace to be added to the array. I can successfully parse the JSON response inside of the onload function, but I want to separate my data code from my UI generation.
Here is a simplified app.js version so that I might be able to understand the concept. And no, I won't pollute the global namespace in my real app.
Titanium.UI.setBackgroundColor('#000');
var myArray = [];
var xhr = Titanium.Network.createHTTPClient();
xhr.onload = function() {
myArray = JSON.parse(this.responseText);
// var data = JSON.parse(this.responseText); // no help
// myArray.push(data); // no help
// return myArray; // no help
};
xhr.onerror = function() {
Titanium.UI.createAlertDialog({ message:"Something has gone terrible wrong."});
};
xhr.open('GET','http://myapp.com/data.json');
xhr.send();
var win = Ti.UI.createWindow();
var view = Titanium.UI.createView({
backgroundColor:'green'
});
var caption = myArray[2].caption;
var label = Ti.UI.createLabel({
color:'white',
text:caption,
textAlign:'center'
});
view.add(label);
win.add(view);
win.open();
Thanks for your patience!
Edit
This produces the the correct result from the user's perspective, but I want to access the array outside the scope of the onload function. I don't want to have UI code mixed with API calls.
xhr.onload = function() {
myArray = JSON.parse(this.responseText);
var caption = myArray[2].caption;
var label = Ti.UI.createLabel({
color:'white',
text:caption,
textAlign:'center'
});
view.add(label);
};
The code is being run asynchronously. The label is attempting to generate before the xhr.onload has begun.
you should fire an event from the onload method of your code.
the event will have a listener in the UI section of your application and it will provide the appropriate separation of http code from the ui code; Something like this
xhr.onload = function() {
myArray = Ti.App.fireEvent("app.updateLabel",
{"responseText":this.responseText});
};
in your UI, view code
Ti.App.addEventListener("app.updateLabel",function(data) {
myArray = JSON.parse(data.responseText);
var caption = myArray[2].caption;
var label = Ti.UI.createLabel({
color:'white',
text:caption,
textAlign:'center'
});
view.add(label);
});
more on events from appcelerator documentation
and I have some examples on my blog, http://blog.clearlyinnovative.com, also
I'm not exactly sure what you're asking but I do see an issue. You are creating a JSON object not an array with myArray = JSON.parse(this.responseText); An object doesn't have the .push() method so that explains the no help comment. I also noticed you defined it as an array then assigned a JSON object to it.

Categories

Resources