I have this simple setup to make an AJAX request and when inspecting memory usage, event listener count and heap use goes up when mashing the button quickly.
Apparently event listener count is expected in Chrome since GC leaves it alone while profiling memory but what about the heap? Am I missing an obvious leak here?
function handleResponse(oReq, success_func) {
return function() {
try {
if (oReq.readyState === XMLHttpRequest.DONE) {
if (oReq.status === 200) {
var data = oReq.responseText;
success_func(data);
}
}
} catch (e) {
console.log('Something went wrong: ' + e.description);
}
}
}
function makeRequest(name, method, data, success_func) {
return function() {
var oReq = new XMLHttpRequest();
oReq.onreadystatechange = handleResponse(oReq, success_func);
oReq.open(method, 'http://localhost:8080/' + name);
oReq.send(data);
}
}
function updateBookList(data) {
console.log(typeof data);
document.getElementById('ajax-content').innerHTML = data;
}
document.getElementById('show-books').addEventListener(
'click', makeRequest('get_books', 'GET', null, updateBookList)
);
You're creating new structures each time you call a function that returns a function (closure-ish). They will stick around until all the vars and objects within them no longer have any references. GC should clear them out after some time.
Javasccript's "prototype" architecture allows you to create instances off of a "master". This allows you to manage the construction and destruction of things.
Plus it allows you to have multiple, unique instances that don't collide with one-another.
You can leverage the ES6 class structure. Or, here's how it would look as a psuedo "class" off the prototype.
this.app = this.app || {};
(function(){
function Req(name, method, data, onSuccess){
// this is kind of like the constructor
this.success_func = onSuccess;
// binding to "this" instance allows the XMLHttpRequest
// to trigger the result within the scope of "this" instance.
// Otherwise, the "onreadystatechange" method will execute
// within the window scope.
this.handleResponse_bound = this.handleResponse.bind(this);
this.makeRequest(name, method, data);
}
var p = Req.prototype;
p.handleResponse = function() {
try {
if (this.oReq.readyState === XMLHttpRequest.DONE) {
if (this.oReq.status === 200) {
var data = this.oReq.responseText;
this.success_func(data);
this.cleanup();
}
}
} catch (e) {
console.log('Something went wrong: ' + e.description);
}
}
p.makeRequest = function(name, method, data) {
this.oReq = new XMLHttpRequest();
this.oReq.onreadystatechange = this.handleResponse_bound;
this.oReq.open(method, 'https://jsfiddle.net/' + name);
this.oReq.send(data);
}
p.cleanup = function(){
this.oReq = null;
this.success_func = null;
this.handleResponse_bound = null;
}
app.Req = Req;
}());
function updateBookList(data) {
console.log(typeof data);
document.getElementById('ajax-content').innerHTML = data;
}
document.getElementById('show-books').addEventListener(
'click', new app.Req('get_books', 'GET', null, updateBookList)
);
Related
var Application;
(function (Application, PhotonSdk) {
(function(Photon) {
Photon.PeerManager = (function () {
var $this;
function PeerManager() {
$this = this;
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connectClosed;
this.peer = new PhotonSdk.PhotonPeer("ws://localhost:9090");
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connecting, this._onConnecting);
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connect, this._onConnect);
}
PeerManager.prototype.establishConnection = function() {
this.peer.connect();
console.log("Photon is establishing connection.");
};
PeerManager.prototype._onConnecting = function() {
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connecting;
PeerManager.prototype._logConnectionState(this.currentStatus); //It work
};
PeerManager.prototype._onConnect = function () {
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connect;
this._logConnectionState(this.currentStatus); //It isn't work :(
};
PeerManager.prototype._logConnectionState = function (state) {
console.log("Photon connection is " + state + ". " + new Date().toTimeString());
};
return PeerManager;
})();
})(Application.Photon || (Application.Photon = {}));
})(Application || (Application = {}), Photon);
If i use this._logConnectionState(this.currentStatus);
i get this._logConnectionState is not a function error, but
PeerManager.prototype._logConnectionState(this.currentStatus);
or
$this._logConnectionState(this.currentStatus);
is work. Why it's happened and how i can do that access through this well doing?
My suggestion is that events _onConnecting and _onConnect are dispatching by PhotonSdk.PhotonPeer instance. Since you have added listeners here:
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connecting, this._onConnecting);
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connect, this._onConnect);
So functions are called with wrong this.
Try this:
function PeerManager() {
$this = this;
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connectClosed;
this.peer = new PhotonSdk.PhotonPeer("ws://localhost:9090");
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connecting, this._onConnecting.bind(this));
this.peer.addPeerStatusListener(PhotonSdk.PhotonPeer.StatusCodes.connect, this._onConnect.bind(this));
}
PeerManager.prototype._onConnect = function () {
this.currentStatus = PhotonSdk.PhotonPeer.StatusCodes.connect;
this._logConnectionState(this.currentStatus); //It isn't work :(
};
Your reference you used
_logConnectionState(this.currentStatus);
on seems to be this:
PeerManager.prototype._onConnect
and not that:
Peer Manager.prototype
basicly this refers to
PeerManager.prototype._onConnect
and
this._logConnectionState
is the same as
PeerManager.prototype._onConnect._logConnectionState
wich is undefined because there is no local value /function for that reference.
As you see "this" only has a local context always being bound to the first object/function it can find while climbing up the scopes.
I cannot find an proper example for the love of my life on how to do this or even if this is possible. Based on my pieced together understanding from fragments of exmaples, I have come up with the following structure
var t = function()
{
this.nestedOne = function()
{
this.nest = function()
{
alert("here");
}
}
}
t.nestedOne.nest();
However this is not working (obviously). I would greatly appreciate if someone could point me in the right direction!
That is simply done with:
var t = {
nestedOne: {
nest: function() {
alert('here');
}
}
};
Your code otherwise doesn't make sense. this inside function doesn't refer to the function itself, it refers to the object context that the function is invoked in. And you are not even invoking the functions in your code.
If I say obj.func() then this inside func will be obj for that call. So assigning this.asd = true will assign true to that object's "asd" property.
If you wanted to do a nested class, it looks very different:
ClassA = (function() {
function ClassA() {
}
ClassA.prototype.method1 = function() {
};
function ClassB() {
}
ClassB.prototype.method1 = function() {
};
return ClassA;
}())
only ClassA can now make instances of ClassB. This should achieve same goals as nested classes in java.
See http://jsfiddle.net/CstUH/
function t(){
function f(){
this.nest = function()
{
alert("here");
}
}
this.nestedOne = new f();
}
var myt=new t();
myt.nestedOne.nest()
Edit 1:
You can also use
new t().nestedOne.nest()
instead of
var myt=new t();
myt.nestedOne.nest()
(http://jsfiddle.net/CstUH/1/)
Edit 2:
Or even more condensed:
function t(){
this.nestedOne = new function(){
this.nest = function(){
alert("here");
}
}
}
new t().nestedOne.nest()
http://jsfiddle.net/CstUH/2/
In JS functions are prime class objects, and you can access them directly in the code [i.e. without using reflection or so].
The code you put inside t body would be performed when actually executing t:
t();
You wrote t.nestedOne,nest(), but t has no nestedOne property - you should do like this:
var t = {
nestedOne : {
nest : function()
{
alert("here");
}
}
};
t.nestedOne.nest();
I advice you to have a trip on John Resig's Learning Advanced JavaScript tutorial, it was very enlightening for me.
A simple callback handler I wrote today as an example of how I do deep nesting. I apologize if it's not the bees knees when it comes to code style, it made the concept a little clearer for me.
function test () {
this.that = this;
this.root = this;
this.jCallback = new Array(new Array()); // 2d
this.jCallbackCount = -1;
this.str = "hello";
// Callback handler...
this.command = {
that : this, // let's keep a reference to who's above us on the food chain
root : this.root, // takes us back to the main object
// add : function() { var that = this; console.log(that.that.str); },
add : function(targetFnc, newFunc) {
var that = this;
var home = that.that; // pretty much root but left in as an example of chain traversal.
var root = this.root; // useful for climbing back up the function chain
// console.log(that.that.str);
home.jCallbackCount++;
// target, addon, active
home.jCallback[home.jCallback.length] = { 'targetFunc' : targetFnc, 'newFunc' : newFunc, 'active' : true, 'id': home.jCallbackCount};
console.log('cbacklength: ' + home.jCallback.length);
console.log('added callback targetFunction:[' + targetFnc + ']');
return home.jCallbackCount; // if we want to delete this later...
},
run : function(targetFnc) {
var that = this;
var home = that.that;
console.log('running callback check for: ' + targetFnc + ' There is : ' + (home.jCallbackCount + 1) + 'in queue.');
console.log('length of callbacks is ' + home.jCallback.length);
for(i=0;i < home.jCallback.length - 1;i++)
{
console.log('checking array for a matching callback [' + targetFnc + ']...');
console.log('current item: ' + home.jCallback[i]['targetFunc'] );
if( home.jCallback[i]['targetFunc'] == targetFnc )
{
// matched!
home.jCallback[i]['newFunc']();
}
// console.log(that.that.jCallback[i].targetFunction);
}
}
};
}
test.prototype = {
say : function () {
var that = this;
console.log('inside');
// that.command('doSay');
that.command.run('doSay');
console.log(that.str);
}
} // end proto
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
var testing = new test();
testing.command.add('doSay', function () { console.log('213123123'); } );
testing.command.add('doSay', function () { console.log('12sad31'); } );
testing.command.add('doSay', function () { console.log('asdascccc'); } );
testing.say();
live:
http://jsfiddle.net/Ps5Uf/
note: to view console output, just open inspector in chrome and click on the "console" tab.
I am trying to write server side for my web application using Node.js. The following code is extracted to simulate the situation. Problem is that the application crashes when trying to access this.actions.length in the actionExecuted "method". The property this.actions is undefined there (this == {} within the scope) even it was defined in the "constructor" (the Request function itself). How to make the actions property accessible from other "methods" as well?
var occ = {
exampleAction: function(args, cl, cb)
{
// ...
cb('exampleAction', ['some', 'results']);
},
respond: function()
{
console.log('Successfully handled actions.');
}
};
Request = function(cl, acts)
{
this.client = cl;
this.actions = [];
this.responses = [];
// distribute actions
for (var i in acts)
{
if (acts[i][1].error == undefined)
{
this.actions.push(acts[i]);
occ[acts[i][0]](acts[i][1], this.client, this.actionExecuted);
}
else
// such an action already containing error is already handled,
// so let's pass it directly to the responses
this.responses.push(acts[i]);
}
}
Request.prototype.checkExecutionStatus = function()
{
// if all actions are handled, send data to the client
if (this.actions == [])
occ.respond(client, data, stat, this);
};
Request.prototype.actionExecuted = function(action, results)
{
// remove action from this.actions
for (var i = 0; i < this.actions.length; ++i)
if (this.actions[i][0] == action)
this.actions.splice(i, 1);
// and move it to responses
this.responses.push([action, results]);
this.checkExecutionStatus();
};
occ.Request = Request;
new occ.Request({}, [['exampleAction', []]]);
The problem is the way you are defining your callback. It's called later so it loses context. You have to either create a closure or properly bind the this. To create a closure:
var self = this;
occ[acts[i][0]](acts[i][1], this.client, function() { self.actionExecuted(); });
To bind to this:
occ[acts[i][0]](acts[i][1], this.client, this.actionExecuted.bind(this));
Either one should work.
I am trying to set a custom error handler for 3rd party plugins/modules in my core library, but somehow, myHandler does not alert the e.message.
Can somebody help me please? thank you
Function.prototype.setErrorHandler = function(f) {
if (!f) {
throw new Error('No function provided.');
}
var that = this;
var g = function() {
try {
var a = [];
for(var i=0; i<arguments.length; i++) {
a.push(arguments[i]);
}
that.apply(null,a);
}
catch(e) {
return f(e);
}
};
g.old = this;
return g;
};
function myHandler(e) {
alert(e.message)
};
// my Core library object
(function(){
if (typeof window.Core === 'undefined') {
var Core = window.Core = function() {
this.addPlugin = function(namespace, obj){
if (typeof this[namespace] === 'undefined') {
if (typeof obj === 'function') {
obj.setErrorHandler(myHandler);
} else if (!!obj && typeof obj === 'object') {
for (var o in obj) {
if (obj.hasOwnProperty(o) && typeof obj[o] === 'function') {
obj[o].setErrorHandler(myHandler);
}
}
}
this[namespace] = obj;
return true;
} else {
alert("The namespace '" + namespace + "' is already taken...");
//return false;
}
};
};
window.Core = new Core();
}
})();
// test plugin
(function(){
var myPlugin = {
init: function() {},
conf: function() {
return this.foo.x; // error here
}
};
Core.addPlugin("myPlugin", myPlugin);
})();
// test
Core.myPlugin.conf(); // supposed to alert(e.message) from myHandler()
setErrorHandler in the above code doesn't set an error handler on a Function, as such. JavaScript does not give you the ability to change the called code inside a Function object.
Instead it makes a wrapped version of the function it's called on, and returns it.
obj.setErrorHandler(myHandler);
Can't work as the returned wrapper function is thrown away, not assigned to anything.
You could say:
obj[o]= obj[o].setErrorHandler(myHandler);
though I'm a bit worried about the consequences of swapping out functions with different, wrapped versions. That won't necessarily work for all cases and could certainly confuse third-party code. At the least, you'd want to ensure you don't wrap functions twice, and also retain the call-time this value in the wrapper:
that.apply(this, a);
(Note: you don't need the manual conversion of arguments to an Array. It's valid to pass the arguments object directly to apply.)
I have some simple javascript that as far as i can tell should work but doesn't.
The code is below
var presenter = new Practicum.Web.TEI.StudentPlacement2009.CreateLetter_class(); //this is a class generated by Ajax.Net
function GetLetters() {
var GetLettersParams = new Object();
GetLettersParams.TemplateType = $('#LetterTypes').val();
var letters = ajaxCall(presenter.GetLetters, GetLettersParams);
createOptions('Templates', letters, 'Id', 'Name', true);
}
function ajaxCall(ajaxMethod, parameters) {
var response = ajaxMethod.call(parameters); //fails here with the message in
if (response.error != null) {
alert('An error has occured\r\n' + response.error.Message);
return;
}
return response.value;
}
this is part of the class Ajax.Net produces.
Practicum.Web.TEI.StudentPlacement2009.CreateLetter_class = function() {};
Object.extend(Practicum.Web.TEI.StudentPlacement2009.CreateLetter_class.prototype, Object.extend(new AjaxPro.AjaxClass(), {
GetLetterTypes: function() {
return this.invoke("GetLetterTypes", {}, this.GetLetterTypes.getArguments().slice(0));
},
GetDegrees: function() {
return this.invoke("GetDegrees", {}, this.GetDegrees.getArguments().slice(0));
},
GetLetters: function(getLettersParams) {
return this.invoke("GetLetters", {"getLettersParams":getLettersParams}, this.GetLetters.getArguments().slice(1));
} ...
Any help would be much appriciated;
Colin G
The first parameter that needs to be passed to Function.call() is the object on which the function is called. Then follow the function parameters as separate values:
func.call(someobj, param1, param2, ...);
To call a function with an array of arguments you should use apply(). apply() also takes the object for which the method should be called as first parameter:
func.apply(someobj, params);
So in your case it would look something like this:
function ajaxCall(ajaxMethod, obj, parameters) {
var response = ajaxMethod.call(obj, parameters);
// ...
}
var letters = ajaxCall(presenter.GetLetters, presenter, GetLettersParams);
You need to pass an object to the first argument of the call method e.g.:
ajaxMethod.call(presenter, parameters);
See http://www.webreference.com/js/column26/call.html
Supertux is right. You could try this to make sure the context is set for "call":
function GetLetters() {
var GetLettersParams = new Object();
GetLettersParams.TemplateType = $('#LetterTypes').val();
var letters = ajaxCall(presenter.GetLetters, presenter, GetLettersParams);
createOptions('Templates', letters, 'Id', 'Name', true);
}
function ajaxCall(ajaxMethod, context, parameters) {
var response = ajaxMethod.call(context, parameters); //Call requires a context
if (response.error != null) {
alert('An error has occured\r\n' + response.error.Message);
return;
}
return response.value;
}
Or you could simplify things quite a bit by not using ajaxCall:
function GetLetters() {
var GetLettersParams = {
TemplateType: $('#LetterTypes').val()
},
response = presenter.GetLetters(GetLettersParams);
if (response.error != null) {
alert('An error has occured\r\n' + response.error.Message);
return;
}
createOptions('Templates', response.value, 'Id', 'Name', true);
}