jQuery Ajax & Memory leak question - javascript

I have web app that does periodic scan operations, and on a specific page, shows the status of those operations (and any previously-completed ones). I have an Ajax request that I send using jQuery, and it returns the same page I'm currently on, modified given a time variable (last updated) to include just the running scans and any recently completed ones.
Apparently, after leaving this open overnight, which isn't a normal use case, on IE8 an 'out of memory at line 112' (nothing notable at line 112 in anything) was returned. I'm trying to figure out what I'm doing wrong, and where it could be leaking.
My question is: since I'm reloading the same page, but only taking a piece of it, are the 'ready' handlers getting rerun or something? For the most part, the active operations table is going to be empty, so it's not like I'm continually increasing the size of the table or something obvious.
function updateActiveScanList()
{
$.ajax({
method: "POST",
url: "ScanList.action",
data: { updatedTime: $('#updatedTime').val() },
success: function(data) {
// Update the active scan list.
$('#activescans').html( $("#activescans", data) );
// the recent scans table update requires more massaging, omitted for brevity,
// since there's nothing else done there, this happens even if nothing else is
// ever inserted.
});
}
$(document).ready(
function(){
setInterval( updateActiveScanList, 30000 );
}
);

You can use a tool like sIEve to detect the things that eat your memory.
I guess the number of used DOM-nodes(they don't need to be a part of the document-tree) will increase with every manipulation.
It would be the best if you forget jQuery for the part of the DOM-manipulation, the methods jQuery uses are known as prone to this issue, while they partial use some "dirty" things like innerHTML.
Can you give an example of what you like to have inside #activescans?

Related

jquery ajax call getting old data when re-used

I have an ajax call that builds a small graph in a popup window. The html for the link is re-used in many different links for different devices on the page. What happens, is that when you click a graph for the first device, you get that device. You click a button for the second device, you get that device, however, if you keep clicking away, after the third click or so, you suddenly start getting only the first device, over and over. I think my variables are being cached in some odd way, and I don't understand:
the HTML:
<a class="bluebtn graphbutton ingraph" href="http://wasat/cgi-bin/rrdjson.cgi?res=3600&start=-24h&end=now-1h&uid=28.7B2562040000" data-uid="28.7B2562040000" data-name="Laundry Room Freezer"></a>
<a class="bluebtn graphbutton ingraph" href="http://wasat/cgi-bin/rrdjson.cgi?res=3600&start=-24h&end=now-1h&uid=28.F7A962040000" data-uid="28.F7A962040000" data-name="Garage Temp"></a>
The code in question:
$(document).ready(function() {
$('.graphbutton').click(function(e) {
var formURL = $(this).attr("href");
var uid = $(this).data("uid");
var name = $(this).data("name");
e.preventDefault();
$.ajax({
url: formURL,
dataType: "json",
cache: false,
context: this,
success: function(data){
console.log("calling mkgraph with uid "+uid+" name " +name);
make_graph(data.data, uid, name);
},
error: function(ts) {
console.log(ts.responseText); }
});
}); /* clickfunc */
}); /*docready */
What happens:
Click freezer:
"calling mkgraph with uid 28.7B2562040000 name Laundry Room Freezer"
Click Garage:
"calling mkgraph with uid 28.F7A962040000 name Garage Temp"
Click Garage again:
"calling mkgraph with uid 28.7B2562040000 name Laundry Room Freezer"
Some of these links are being manufactured by the make_graph() function. I'm a bit worried that this is the issue, and somehow the ajax thing needs to be re-initialized after doing this?
By request, the relevant code in make_graph() that I think is causing my issue here. Basically, I'm editing the buttons in the css popup on the fly, and I think this is creating a wierd situation where the ajax binding is bound to the old href, and not being updated, even though the link is correct in the produced html. This is consistent with the effect where the binding only gets mangled on the third attempt.
$(".ingraph").each(function() {
this.href = $(this).attr("href").replace(/uid=.*/g, 'uid=' + uid);
this.setAttribute('data-uid' ,uid);
if (devname.length > 0) {
this.setAttribute('data-name', devname);
}
});
EDIT: adding a long answer:
I have multiple buttons on the main page. Each one specifies a "uid" that gets fed to rrdjson.cgi, which takes the uid and finds the data for that device, and returns it as json. When make_graph() recieves this json data, it populates a css popup, with the graph, and edits 5 buttons so they reference that UID. Those 5 buttons change the timescale of the graph by re-requesting the data from rrdjson.cgi.
What I am worried is happening, is that I click on the frige, it changes the uid's of the buttons inside the popup to reference the frige. Then I close that, click on the garage, it also changes the uid's and correctly shows the garage data. Then I click on one of the buttons inside the popup for the garage, and poof, I get the refrigerator again. I suspect that ajax "remembers" the old values for $(this).attr("href") etc and passes those values to the code, rather than re-reading the contents of the HTML. (perhaps instead of HTML, I meant DOM there, I'm a little vauge on the difference, but I suspect I meant DOM)
Maybe the answer is to somehow un-register the ajax binding to those buttons and re-register it every time make_graph() changes them? How would I do the un-register? .off() ? .unbind() ?
After much gnashing of teeth, and google, I have answered my own question.
https://forum.jquery.com/topic/jquery-data-caching-of-data-attributes
Turns out, jquery caches "data" types, but not attr types. So when I do:
uid = $(this).data("uid");
vs
uid = $(this).attr("data-uid");
I get wildly different results. I guess the moral of the story is that .data is super evil.. :)
If you add a random value to your url like
var formURL = $(this).attr("href")+"?rv="+Math.random();
you'll force the ajax call to reload the URL. You can use the cache property (set it to false) JQuery will load the data again, but any proxy may send a cached version.
(Please check that there are no other attributes set in the url, otherwise set "&rv="+Math.random(); (& instead of ?) use
var formURL = $(this).attr("href");
formURL + (formURL.indexOf("?") > 0 ? "&rv=" : "?rv=" )+ Math.random();
Your problem should not have something to do with make_graph() as uid and name depend on $('.graphbutton')
(if not make_graph(), or some other function, changes the attributes of your buttons)

ExtJS 4 grid and some problems with mask

I have a not too big grid (30x20) with numbers in cells. I have to display all, calculate them in different ways (by columns, rows, some cells, etc.) and write values to some cells. This data is also written and read from db table fields. Everything is working, excluding simple (theoretically) mask tools.
In time of e.g. writing data to the field in the table I try to start mask and close it on finish. I used such a “masks” very often but only in this situation I have a problem and can’t solve it.
I prepare this mask the following way:
msk = new Ext.LoadMask(Ext.getBody(), { msg: "data loading ..." });
msk.show();
[writing data loops]
msk.hide();
msk.destroy();
I also tried to use grid obiect in place of Ext.getBody(), but without result.
I found also that the program behaves in a special way – loops which I use to write data to the table field are "omitted" by this mask, and it looks like loops are working in the background (asynchronously).
Would you be so kind as to suggest something?
No, no, no, sorry guys but my description isn’t very precise. It isn’t problem of loading or writing data to the database. Let’s say stores are in the memory but my problem is to calculate something and write into the grid. Just to see this values on the screen. Let me use my example once again:
msk = new Ext.LoadMask(Ext.getBody(), { msg: "data loading ..." });
msk.show();
Ext.each(dataX.getRange(), function (X) {
Ext.each(dataY.getRange(), function (Y) {
…
X.set('aaa', 10);
…
}
msk.hide();
msk.destroy();
And in such a situation this mask isn’t visible or is too fast to see it.
In the mean time I find (I think) a good description of my problem but still can’t find a solution for me. When I use e.g. alert() function I see this mask, when I use delay anyway, mask is too fast. Explanation is the following:
The reason for that is quite simple - JS is single threaded. If you modify DOM (for example by turning mask on) the actual change is made immediately after current execution path is finished. Because you turn mask on in beginning of some time-consuming task, browser waits with DOM changes until it finishes. Because you turn mask off at the end of method, it might not show at all. Solution is simple - invoke store rebuild after some delay.*
I have no idea how is your code looks in general but this is some tip that you could actually use.
First of all loading operations are asynchronously so you need to make that mask show and then somehow destroy when data are loaded.
First of all check if in your store configuration you have autoLoad: false
If yes then we can make next step:
Since Extjs is strongly about MVC design pattern you should have your controller somewhere in your project.
I suppose you are loading your data on afterrender or on button click event so we can make this:
In function for example loadImportantData
loadImportantData: function(){
var controller = this;
var store = controller.getStore('YourStore'); //or Ext.getStore('YourStore'); depends on your configuration in controller
var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
myMask.show();
store.load({
callback: function (records, operation, success) {
//this callback is fired when your store load all data.
//then hide mask.
myMask.hide();
}
});
}
When data is loaded your mask will disappear.
If you have a reference to the grid, you can simply call grid.setLoading(true) to display a loading mask over the grid at any time.

Meteor: Slow template render blocks method return value

I'm experiencing a very strange issue with Meteor whereby a single page is taking a very long time to load. I've gone down numerous routes to try and troubleshoot before identifying a cause which isn't really making sense to me.
Fundamentally I have a two-step process that collects some initial data and then inserts a couple of hundred records into the database. On the second page I then need to be able to assign users to each of the new records. I can also go back into the same page and edit the assigned users so in this second scenario the inserts have already happened - same lag.
The insert code is relatively straightforward.
Client Side
setup assessment meta-data & create new assessment record + items:
// set updatingAssessment variable to stop subs re-loading as records are inserted
Session.set('updatingAssessment', true);
Meteor.call('insertAssessment', assessment, function(err, result) {
if (err) { showError(err.reason); }
var assessmentId = result.toHexString();
// some code excluded to get 'statements' array
Meteor.call('insertAssessmentItems',
assessmentId,
statements,
function(err, result) {
Session.set('updatingAssessment', false);
});
}
// hide first modal and show 2nd
Session.set("assessmentStage1", false);
Session.set("assessmentStage2", true);
Server methods
Volume wise, we're inserting 1 assessment record and then ~200 assessment items
insertAssessment: function(assessment) {
this.unblock();
return Assessments.insert({
<<fields>>
}, function(err, result) { return result; });
},
insertAssessmentItems: function(assessmentId, statements) {
this.unblock();
for (i = 0; i < statements.length; i++) {
AssessmentItems.insert({
<<fields>>
}, function(err, result) { });
}
}
Initially I tried numerous SO suggestions around not refreshing subscriptions while the inserts were happening, using this.unblock() to free up DDP traffic, making queries on the slow page non-reactive, only returning the bare minimum fields, calling the inserts asynchronously, etc, etc... all of which made no difference with either the initial insert, or subsequent editing of the same records (i.e go out of the page and come back so the inserts have already happened).
After commenting out all database code and literally returning a string from the meteor method (with the same result / lag) I looked at the actual template generation code for the html page itself.
After commenting out various sections, I identified that a helper to build up a dropdown list of users (repeated for each of those ~200 records) was causing the massive spike in load time. When I commented out the dropdown creation the page load happens in <5s which is within a more acceptable range although still slower than i would like.
Two oddities with this, which I was hoping somebody could shed some light on:
Has anything changed recently in Meteor that would have caused a slowdown as I haven't significantly changed my code for a couple of months and it was working before, albeit still around 10 seconds to fully render the page. The page also used to be responsive when loaded, whereas now the CPU is stuck at 100% and selecting users is very laggy.
Why is the actual template loading blocking my server method returns? There is no doubt I need to re-write my template to be more efficient but I spent a lot of time trying to figure out why my server methods were not returning values quickly. I even analysed the DDP traffic and there was no activity while it was waiting for the template to load - no reloaded subs, no method returns, nothing.
Thanks in advance,
David

storing a variable in localStorage is too slow

I have two jQuery mobile pages (#list and #show). There are several items on the #list page with different IDs. If I click on item no.5, the ID no5 will be stored in localStorage and I will be redirected to page #show
Now the problem:
Storing the ID in localStorage works, but the next page shows me not the item no.5, but it shows me an old item, that was in the localStorage before.
script from page #list
localStorage.setItem("garageID", $(this).attr('id'));
window.location.replace("#show");
I encountered this problem too (and not on a mobile : on Chromium/linux).
As there doesn't seem to be a callback based API, I "fixed" it with a timeout which "prevents" the page to be closed before the setItem action is done :
localStorage.setItem(name, value);
setTimeout(function(){
// change location
}, 50);
A timeout of 0 might be enough but as I didn't find any specification (it's probably in the realm of bugs) and the problem isn't consistently reproduced I didn't take any chance. If you want you might test in a loop :
function setLocalStorageAndLeave(name, value, newLocation){
value = value.toString(); // to prevent infinite loops
localStorage.setItem(name, value);
(function one(){
if (localStorage.getItem(name) === value) {
window.location = newLocation;
} else {
setTimeout(one, 30);
}
})();
}
But I don't see how the fact that localStorage.getItem returns the right value would guarantee it's really written in a permanent way as there's no specification of the interruptable behavior, I don't know if the following part of the spec can be legitimately interpreted as meaning the browser is allowed to forget about dumping on disk when it leaves the page :
This specification does not require that the above methods wait until
the data has been physically written to disk. Only consistency in what
different scripts accessing the same underlying list of key/value
pairs see is required.
In your precise case, a solution might be to simply scroll to the element with that given name to avoid changing page.
Note on the presumed bug :
I didn't find nor fill any bug report as I find it hard to reproduce. In the cases I observed on Chromium/linux it happened with the delete operation.
Disclaimer: This solution isn't official and only tested for demo, not for production.
You can pass data between pages using $.mobile.changePage("target", { data: "anything" });. However, it only works when target is a URL (aka single page model).
Nevertheless, you still can pass data between pages - even if you're using Multi-page model - but you need to retrieve it manually.
When page is changed, it goes through several stages, one of them is pagebeforechange. That event carries two objects event and data. The latter object holds all details related to the page you're moving from and the page you're going to.
Since $.mobile.changePage() would ignore passed parameters on Multi-page model, you need to push your own property into data.options object through $.mobile.changePage("#", { options }) and then retrieve it when pagebeforechange is triggered. This way you won't need localstorage nor will you need callbacks or setTimeout.
Step one:
Pass data upon changing page. Use a unique property in order not to conflict with jQM ones. I have used stuff.
/* jQM <= v1.3.2 */
$.mobile.changePage("#page", { stuff: "id-123" });
/* jQM >= v1.4.0 */
$.mobile.pageContainer.pagecontainer("change", "#page", { stuff: "id-123" });
Step two:
Retrieve data when pagebeforechange is triggered on the page you're moving to, in your case #show.
$(document).on("pagebeforechange", function (event, data) {
/* check if page to be shown is #show */
if (data.toPage[0].id == "show") {
/* retrieve .stuff from data.options object */
var stuff = data.options.stuff;
/* returns id-123 */
console.log(stuff);
}
});
Demo

ExtJs 4.2 ref-selector vs Ext.getcmp() vs Ext.ComponentQuery.query()

I was asked to develop a tab panel with 6 tabs, each having 30 to 40 elements. Each tab is acting as a form in accumulating the details of a person and the last tab is a Summary page which displays all the values entered in the first five tabs. I was asked to provide summary as a tab because, the user can navigate to summary tab at any instance and look at the details entered by him/ or glace the summary. i am following ExtJs MVC pattern. Payload is coming from / going to Spring MVC Application. (JSON)
Using tab change event in controller and if the newtab is summary I am rendering the page with show hide functionality.
Method 1 :In controller I have used Ext.getCmp('id of each element inside the tabs') and show hide the components in summary tab based on the value entered by the user. This killed my app in IE8 popping a message saying that the "script is slow and blah blah..." i had to click on NO for 5 to 6 times for the summary tab to render and display the page.
Method 2 :In controller I used ref and selectos to acccess all the items in tabs. I have used itemId for each and every field in summary tab. like this.getXyz().show(). I thought it would be fast. Yes it was in Google chrome. but my app in IE8 is slow compared to goolge chrome/firefox
Any suggestions regarding this and plan to decrease page render time. The summary page has more than 1000 fields. Please feel free to shed ur thoughts or throw some ideas on this.
thank you!!
I've got a few suggestions you can try. First, to answer your title, I think the fastest simple way to lookup components in javascript is to build a hash map. Something like this:
var map = {};
Ext.each(targetComponents, function(item) {
map[item.itemId] = item;
});
// Fastest way to retrieve a component
var myField = map[componentId];
For the rendering time, be sure that the layout/DOM is not updated each time you call hide or show on a child component. Use suspendLayouts to do that:
summaryTabCt.suspendLayouts();
// intensive hide-and-seek business
// just one layout calculation and subsequent DOM manipulation
summaryTabCt.resumeLayouts(true);
Finally, if despite your best efforts you can't cut on the processing time, do damage control. That is, avoid freezing the UI the whole time, and having the browser telling the user your app is dead.
You can use setTimeout to limit the time your script will be holding the execution thread at once. The interval will let the browser some time to process UI events, and prevent it from thinking your script is lost into an infinite loop.
Here's an example:
var itemsToProcess = [...],
// The smaller the chunks, the more the UI will be responsive,
// but the whole processing will take longer...
chunkSize = 50,
i = 0,
slice;
function next() {
slice = itemsToProcess.slice(i, i+chunkSize);
i += chunkSize;
if (slice.length) {
Ext.each(slice, function(item) {
// costly business with item
});
// defer processing to give time
setTimeout(next, 50);
} else {
// post-processing
}
}
// pre-processing (eg. disabling the form submit button)
next(); // start the loop
up().down().action....did the magic. I have replaced each and every usage of Ext.getCmp('id'). Booooha... it's fast and NO issues.
this.up('tabpanel').down('tabName #itemIdOfField').actions.
actions= hide(), show(), setValues().
Try to check deferredRender is true. This should only render the active tab.
You also can try a different hideMode. Especially hideMode:'offsets ' sounds promising
Quote from the sencha API:
hideMode: 'offsets' is often the solution to layout issues in IE specifically when hiding/showing things
As I wrote in the comment, go through this performance guide: http://docs.sencha.com/extjs/4.2.2/#!/guide/performance
In your case, this will be very interesting for you:
{
Ext.suspendLayouts();
// batch of updates
// show() / hide() elements
Ext.resumeLayouts(true);
}

Categories