this is my first post, but i'm excited to join this community. I have a question regarding JavaScript which I am completely stumped about.
I'm writing a JavaScript application which pulls data from a server using ajax and adds it to a chart. I'm using Jquery and Highcharts as the framework and then writing my own JavaScript 'wrapper' around Highcharts to produce the interface.
When the processData function get called back with the jSON response, it begins with i=1, even though i shouldn't even be initialized or even declared yet. Other variables are set as well. (I know this from using chrome developer tools to debug). This makes my loop not execute and none of my data gets added to the chart.
I don't know how much code to show, but these are the most relevant parts. I can add more if needed.
function getData(series, min, max, numpts) {
if (series === undefined) {
console.log("error on getData");
return;
}
var request = {};
request.series = series;
if (min !== undefined) {
request.start = min;
} //in seconds
if (max !== undefined) {
request.end = max;
}
if (numpts !== undefined) {
request.numpts = numpts;
}
$.getJSON('/data', request, processData);
return;
}
function processData(data) {
// handle the data after it comes back from an ajax request
var curSeries,
chartSeries,
curPoint;
for (var i = 0; i < data.length; i ++) {
curSeries = data[i];
chartSeries = chart.get(curSeries.name);
if (chartSeries === null) {
//alert("oops");
chart.addSeries(curSeries);
} else {
for (var j = 0; j < curSeries.data.length; j ++) {
curPoint = curSeries.data[j];
chartSeries.addPoint(curPoint, false);
}
}
}
chart.redraw();
}
These are both methods of a class I declared called graph.
Thanks if anyone has any ideas!
-Matt P
I'd console inspect your data object to make sure it's what you expect, as that loop should be working fine even if i is pre-declared: you're assigning 0 to it at the beginning of the loop, anyway.
The only reason I can think of that i would be defined and initialized before you defined and initialized it is if somewhere else in your codebase you don't initialize the i with the var keyword. Doing that would dump it into the global scope (the window object), making it available via closure to any and every function in your codebase.
If it's not in one of your files, it may be in the highcharts graphing library (in which case run very quickly away from said library).
Related
I am doing some research on a running website, and the behavior of the code just surprises me.
They defined a function named '__jsload' and bind it to window.
window._jsload = function(hJ, hK) {
var i = d2.getModuleInfo(hJ);
i.status = d2.Request.LOADED;
if (hK !== "") {
d2.run(hJ, hK)
} else {
if (window.map) {
var e = new a6("ongetmodules_fail");
e.moduleName = hJ;
window.map.fire(e)
}
var T = document.createElement("script");
var hI = d2.MD5Mapping[hJ];
T.src = d2.Config.jsModPath + hJ + "_" + hI + ".js";
document.getElementsByTagName("head")[0].appendChild(T)
}
};
In the run function, there's only an eval(), it seems nothing is stored for future use.
run: function(T, hI) {
var hM = this.getModuleInfo(T);
var hP = this.Dependency[T];
if (hP) {
for (var hK = 0; hK < hP.length; hK++) {
var hL = this.getModuleInfo(hP[hK]);
if (hL.status !== this.Request.COMPLETED) {
hL.modsNeedToRun.push({
name: T,
code: hI
});
return
}
}
}
try {
eval(hI)
} catch (hN) {
return
}
hM.status = this.Request.COMPLETED;
for (var hK = 0, hJ = hM.callbacks.length; hK < hJ; hK++) {
hM.callbacks[hK]()
}
hM.callbacks.length = 0;
for (hK = 0; hK < hM.modsNeedToRun.length; hK++) {
var hO = hM.modsNeedToRun[hK];
this.run(hO.name, hO.code)
}
hM.modsNeedToRun.length = 0
},
Then all the module are written in the following manner.
/**/_jsload&&_jsload(
// this should be the module name
'glcommon',
// this should be the module content
'function aO(e,i)......'
)
I have a little experience in coding in python, so I thought those loaded modules will be special objects, and I can access functions defined in the module by just calling them.
import pandas as pd
pd.DataFrame()
And I searched some references on coding in javascript, they mentioned something like 'import/export' and maybe 'require'. After importing the module, I can call exported function just by calling the name.
// in sayHi.js
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
// in main.js
import {sayHi} from './sayHi.js';
alert(sayHi); // function...
sayHi('John'); // Hello, John!
None of these things happens in that website I'm researching on. There's even no 'export' in the whole module text.
So, here rises my question.
Is it possible to call the function in a module in the web console? If it's not bound to the window object, will it still be possible?
We all know that currently a lot of web pages are dynamic, when a certain event happens, a certain function relate to it should be called. So, where are those functions saved? Are they still reachable during the web is running?
3.How can I debug modules like the above ones, when you can't get a pretty printed version of code and make some breakpoints? I tried to go step by step, but all the functions go into the text versioned module, and I can't make a breakpoint.
Here's a simple case:
let html = `<<some huge html file>>`
var libxmljs = require("libxmljs");
class MyObject{
constructor(html){
this.doc = libxmljs.parseHtml(html);
this.node = this.doc.root()
}
}
let obj
for(var i = 0; i < 100000; i++){
obj = new MyObject(html)
// if I uncomment the next line it works fine
// obj.node = null
console.log(i)
}
When I run it the script quickly runs out of memory, apparently because obj.node isn't getting garbage collected properly. How can I make sure that happens without explicitly setting it to null when I think I'm done with it?
The object .root() returns seems to GC more if you don't store the reference specifically in a class instance. The memory usage still seems fairly leaky as the full amount of heap allocated is never reclaimed. Node itself seems to use about twice as much memory than lives on the heap to take care of the native libxml code. Maybe raise an issue on libxmljs as this quacks like a bug.
Not storing the object in the class instance but passing it through works better.
class MyObject{
constructor(){
this.doc = libxmljs.parseHtml(html)
}
get node(){
return this.doc.root()
}
}
Using a plain object works better too.
function myObject(){
let doc = libxmljs.parseHtml(html)
let node = doc.root()
return {
doc: doc,
node: node,
}
}
As an alternative maybe try one of the JS based parsers.
TL;DR: It's the library and not node which is an issue.
Long answer
Here is a slightly modified code
var heapdump = require('heapdump');
const fs = require('fs');
var libxmljs = require("libxmljs");
const content = fs.readFileSync('./html2.htm');
let id = 0;
class MyObject{
constructor(){
this.doc = libxmljs.parseHtml(content);
this.node = this.doc.root()
}
}
let obj;
function createObject () {
obj = new MyObject(content);
};
try {
for(var i = 0; i < 3000; i++){
createObject();
// if I uncomment the next line it works fine
// obj.node = null
console.log(i);
if (i === 50) {
heapdump.writeSnapshot('/Users/me/3.heapsnapshot');
}
if (i === 100) {
heapdump.writeSnapshot('/Users/me/4.heapsnapshot');
}
if (i === 150) {
heapdump.writeSnapshot('/Users/me/5.heapsnapshot');
}
}
console.log('done');
}
catch(e) {
console.log(e);
}
Below is the relevant section of the heapdump diff we took in the code (3 and 4)
And even clear when we look at 4 and 5 heapdump
Few thing that we can conclude from these heapdumps:
There is no memory leak in the JS part.
The size of the heapdump does not match the size of the process we see on htop/top/activity monitor depending on your OS. (12 MB of heapdump versus few Gb in RAM)
Heapdump will only give us memory leak which are in JS. Since this library has c code, heapdump will not capture leaks which will be there.
I am not sure how we can capture the dump from that library or why setting it to null allows the memory to be freed but it should be safe to assume that node gc is doing everything it can.
Hope this helps
After reading the following article,
http://javascriptplayground.com/blog/2012/04/javascript-module-pattern/
I have decided to start implementing modules in my JS.
Unfortunately, the module I am using does not seem to be keeping the private variable private,
var popoverOptionsModule = (function() {
var _stopAskingList = [];
var addToStopAskingList = function(itemToAdd) {
if (_stopAskingList.indexOf(itemToAdd) === -1){
_stopAskingList.push(itemToAdd);
}
}
var getStopAskingList = function() {
return _stopAskingList;
}
return {
addToStopAskingList: addToStopAskingList,
getStopAskingList: getStopAskingList,
};
})();
popoverOptionsModule._stopAskingList = 4;
console.log(popoverOptionsModule._stopAskingList);
As you can see, I am able to change the value of
popoverOptionsModule._stopAskingList and log the update to the console... I thought this was not supposed to happen. Thanks for your help!
JS is completely dynamically typed, so when you have the line
popoverOptionsModule._stopAskingList = 4;
You've just created this variable and assigned it a value, hence why the next line succeeds. If you didn't have this line, then the subsequent console.log would report undefined. This code would work too
popoverOptionsModule._abc = 4;
console.log(popoverOptionsModule._abc);
Remember that this isn't actually a private variable in the same way that OO languages implement protection levels, rather it's just an API pattern that attempts to hide it from the caller.
I'm currently trying to make a HTML/JavaScript Windows 8 modern application in which I want to access a local XML file that is in the installation directory.
After reading many ideas and code snippets around the web, I came up with a convoluted asynchronous method of accessing the file, which works. However, is this the best/correct way to do something as simple as accessing a local XML file?
Additionally, I'd like to be able to have a function load the xml file, and save the XMLDocument object as a "global" variable, so that on button presses and other triggers, the XMLDocument object can be accessed and parsed. This is where all the problems start, since one method is async, and then the variables are undefined, etc....
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/reader/reader.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
// TODO: Initialize the page here.
var button = document.getElementById("changeText");
button.addEventListener("click", this.buttonClickHandler, false);
var dropdown = document.getElementById("volumeDropdown");
dropdown.addEventListener("change", this.volumeChangeHandler, false);
var loadSettings = new Windows.Data.Xml.Dom.XmlLoadSettings;
loadSettings.prohibitDtd = false;
loadSettings.resolveExternals = false;
//previous attempt, also didn't work:
//this.xmlDoc = null;
//this.loadXMLdoc(this, this.testXML);
//also not working:
this.getXmlAsync().then(function (doc) {
var xmlDoc = doc;
});
//this never works also, xmlDoc always undefined, or an error:
//console.log(xmlDoc);
},
buttonClickHandler: function (eventInfo) {
// doesn't work, xmlDoc undefined or error:
console.log(xmlDoc);
},
volumeChangeHandler: function (eventInfo) {
var e = document.getElementById("volumeDropdown");
// of course doesn't work, since I can't save the XMLDocument object into a variable (works otherwise):
var nodelist2 = xmlDoc.selectNodes('//volume[#name="volumeName"]/chapter/#n'.replace('volumeName', list[0]));
var volumeLength = nodelist2.length;
for (var index = 0; index < volumeLength; index++) {
var option = document.createElement("option");
option.text = index + 1;
option.value = index + 1;
var volumeDropdown = document.getElementById("chapterDropdown");
volumeDropdown.appendChild(option);
}
},
getXmlAsync: function () {
return Windows.ApplicationModel.Package.current.installedLocation.getFolderAsync("books").then(function (externalDtdFolder) {
externalDtdFolder.getFileAsync("book.xml").done(function (file) {
return Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file);
})
})
},
loadXMLdoc: function (obj, callback) {
var loadSettings = new Windows.Data.Xml.Dom.XmlLoadSettings;
loadSettings.prohibitDtd = false;
loadSettings.resolveExternals = false;
Windows.ApplicationModel.Package.current.installedLocation.getFolderAsync("books").then(function (externalDtdFolder) {
externalDtdFolder.getFileAsync("book.xml").done(function (file) {
Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file, loadSettings).then(function (doc) {
var nodelist = doc.selectNodes("//volume/#name");
var list = [];
for (var index = 0; index < nodelist.length; index++) {
list.push(nodelist[index].innerText);
};
for (var index = 0; index < list.length; index++) {
var option = document.createElement("option");
option.text = list[index] + "new!";
option.value = list[index];
var volumeDropdown = document.getElementById("volumeDropdown");
volumeDropdown.appendChild(option);
};
var nodelist2 = doc.selectNodes('//volume[#name="volumeName"]/chapter/#n'.replace('volumeName', list[0]));
var volumeLength = nodelist2.length;
for (var index = 0; index < volumeLength; index++) {
var option = document.createElement("option");
option.text = index + 1;
option.value = index + 1;
var volumeDropdown = document.getElementById("chapterDropdown");
volumeDropdown.appendChild(option);
};
obj.xmlDoc = doc;
callback(obj);
})
})
});
},
initializeXML: function (doc, obj) {
console.log("WE ARE IN INITIALIZEXML NOW")
obj.xmlDoc = doc;
},
testXML: function (obj) {
console.log(obj.xmlDoc);
},
});
})();
In summary with all these complicated methods failing, how should I go about doing something as simple as loading an XML file, and then having it available as an object that can be used by other functions, etc.?
Thanks for your help!
PS:
I'm very new to JavaScript and Windows 8 Modern Apps/ WinAPIs.
Previous experience all in Python and Java (where doing this is trivial!).
There are a couple of things going on here that should help you out.
First, there are three different loading events for a PageControl, corresponding to methods in your page class. The ready method (which is the only one the VS project template includes) gets called only at the end of the process, and is thus somewhat late in the process for doing an async file load. It's more appropriate to do this work within the init method, which is called before any elements have been created on the page. (The processed method is called after WinJS.UI.processAll is complete but before the page has been added to the DOM. ready is called after everything is in the DOM.)
Second, your getXMLAsync method looks fine, but your completed handler is declaring another xmlDoc variable and then throwing it away:
this.getXmlAsync().then(function (doc) {
var xmlDoc = doc; //local variable gets discarded
});
The "var xmlDoc" declares a local variable in the handler, but it's discarded as soon as the handler returns. What you need to do is assign this.xmlDoc = doc, but the trick is then making sure that "this" is the object you want it to be rather than the global context, which is the default for an anonymous function. The pattern that people generally use is as follows:
var that = this;
this.getXmlAsync().then(function (doc) {
that.xmlDoc = doc;
});
Of course, it's only after that anonymous handler gets called that the xmlDoc member will be valid. That is, if you put a console.log at the end of the code above, after the });, the handler won't have been called yet from the async thread, so xmlDoc won't get be valid. If you put it inside the handler immediately after that.xmlDoc = doc, then it should be valid.
This is all just about getting used to how async works. :)
Now to simplify matters for you a little, there is the static method StorageFile.getFileFromApplicationUriAsync which you can use to get directly to in-package file with a single call, rather than navigating folders. With this you can load create the XmlDocument as follows:
getXmlAsync: function () {
return StorageFile.getFileFromApplicationUriAsync("ms-appx:///books/book.xml").then((function (file) {
return Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file);
}).then(function (xmlDoc) {
return xmlDoc;
});
}
Note that the three /// are necessary; ms-appx:/// is a URI scheme that goes to the app package contents.
Also notice how the promises are chained instead of nested. That's typically a better structure, and one that allows a function like this to return a promise that will be fulfilled with the last return value in the chain. This can then be used with the earlier bit of code that assigns that.xmlDoc, and you avoid passing in obj and a callback (promises are intended to avoid such callbacks).
Overall, if you have any other pages in your app to which you'll navigate, you'll really want to load this XML file and create the XmlDocument once for the app, not with the specific page. Otherwise you'd be reloading the file every time you navigate to the page. For this reason, you could choose to do the loading on app startup, not page load, and use WinJS.Namespace.define to create a namespace variable in which you store the xmlDoc. Because that code would load on startup while the splash screen is visible, everything should be ready when the first page comes up. Something to think about.
In any case, given that you're new to this space, I suggest you download my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, where Chapter 3 has all the details about app startup, page controls, and promises (after the broader introductions of Chapters 1 and 2 of course).
I have two qml files. I would connect a signal with javascript of the one qml file with a slot of the other qml file, but it seems not to work. The slot in the second file will not execute.
Here I connect the signal with the slot (in main.qml, GridLayout, onComplete):
var component = Qt.createComponent("Field.qml")
for (var i=0; i<30; i++) {
var object = component.createObject(mainGridLayout);
object.boardX = i % 5;
object.boardY = i / 5;
object.board = gameboard;
gameboard.fieldChanged.connect(object.fieldChange); //<-- Connect
}
Here I defined the signal (in main.qml):
Gameboard {
id: gameboard
signal fieldChanged(int x, int y, int val) //<-- Signal defined here
onBoardFieldChanged: { //<-- Works fine from C++
fieldChanged(x,y,value);
}
}
Here I defined the slot (in Field.qml):
function fieldChange(x, y, val){ //<-- Slot method
if (boardX==x && boardY == y) caption.text = val;
}
There are no errors in the console, but the slot did not execute. What did I do wrong? Thank you for your help.
Sorry I was a bit stupid. The code I posted is correct and works well. The problem was, that the signal was emitted before it was connected to the slot.