Firebase high memory and cpu usage - performance

I have the following controller
var myApp = angular.module("MyApp", ["firebase"]);
function MyController($scope, $firebase) {
var rootRef = new Firebase("vivid-fire-447.firebaseio.com/products");
// Automatically syncs everywhere in realtime
$scope.products = $firebase(rootRef);
$scope.products.$on('loaded', function(){
var index = $scope.products.$getIndex();
$scope.total = function(){
var total = 0;
index.forEach(function(i){
total += $scope.products.$child(i).price;
});
return total;
};
});
}
and the following html
<div ng-app="MyApp">
<div ng-controller="MyController">
<ul>
<li ng-repeat="product in products | orderByPriority">{{product.code}}</li>
</ul>
<div>Total = {{total()}}</div>
</div>
</div>
The problem is that when I show the total price of products Chrome cpu gets pegged at 100% and memory usage starts climbing rapidly and eventually the tab hangs.
I noted using AngularJS Batarang that total method gets called continuosly?
How can I get the total price of all products in an efficient way?
JsFiddle

I tried your example and it ran without issue; it loaded in less than 1 second. I don't think your code/data shown above accurately captured the problem. However, I did note a couple potential issues you would start with.
The definition for $scope.getTotal is inside the loaded event for $scope.products which means that function may exist the first time Angular tries to compile this page. You can correct that by giving it an empty array initially, and moving it out of the loaded callback:
$scope.products = $firebase(rootRef);
var index = [];
$scope.products.$on('loaded', function(){
index = $scope.products.$getIndex();
});
$scope.total = function(){ /*...*/ }
The code creates a new synchronized connection to Firebase for each record by calling $child. There is no need for this since all the data is already present in the parent object. In general, you don't need to use $child as it's for some specialized use cases:
total += $scope.products[key].price; // far more efficient!
Here's a fiddle demonstrating the changes. On my box it loads in less than half a second, even with the overhead of fiddle's containing code.
http://jsfiddle.net/katowulf/Q6VPx/

Related

Is there a way to run code after knockoutjs render only knowing the binding context?

I'm working on a module in a CMS' backend. I'm trying to 'hook in' to their knockout bindings and run code when they are finished rendering. So far I've had no luck.
I have however, attached to the different data-components and obtained knockout data.
I've had many failed attempts, but so far, I have this which is returning a binding context.
var bindingContext = ko.contextFor(jQuery('div[data-component="customer_form.areas"]').get(0));
Does anyone know of a way I can use this to somehow attach an observer to watch for the rendering to finish? I'll admin, I'm new to knockout. I'm not creating the view models, nor the templates. I can't add the afterRender to the template like I think should be done.
Like you said, this should be done using afterRender. All other methods feel hacky, because you'll never know when knockout will re-render (parts of) the ui.
I can't think of a reason why you'd need such a work around, but who am I to judge..
The only approach I can think off, is to use the MutationObserver. Here's an example:
var bindingContextElement = document.querySelector("ul");
var renderThrottle = 300;
var renderCycle = null;
var onRenderComplete = function() {
var pre = document.createElement("pre");
var msg = new Date().toLocaleString() + ": finished rendering";
pre.appendChild(document.createTextNode(msg));
document.body.appendChild(pre);
}
// Observe mutations to element, call onRenderComplete after 300ms of no mutations
var observer = new MutationObserver(function(mutations) {
clearTimeout(renderCycle);
renderCycle = setTimeout(onRenderComplete, renderThrottle);
});
var config = {
childList: true
};
observer.observe(bindingContextElement, config);
ko.applyBindings({
items: ko.observableArray([1, 2, 3])
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: items">
<li data-bind="text: $data"></li>
</ul>
<button data-bind="click: function() { items.push(items().length + 1)}">add</button>
This code listens to any mutations in the <ul> element, which is controlled by knockout. Once changes start happening to the element or its children, it attempts to log a "Rendered" message. It's only allowed to log this message if there are no further changes for 300ms.
Have a look at the docs to determine your config object and which elements to watch... And please keep in mind that things might get out of hand if stuff gets more complicated than this example...

Meteor infinite loop with Session variables

i'm rather new to Meteor and have a problem, where can't figure out how to solve it.
I want to store dates in a collection. I can pickup the place of the meeting using google maps, which gives me a String with the coordinates.
I reverse geocode the coordinates with jaymc:google-reverse-geocode which is basically working (i can console.log the results).
When using Session variables i can output the result, but they keep changing itself. The entrys get there result, then first and second entry change their result, then they change again and so on.
I tried to use ReactiveVar and ReactiveDict but with no result. I can't get any results returned from the reverseGeocode function.
Here's the code:
{{#each termine}}
<div class="listTermine">
<p class="title">{{title}}</p>
<p class="desc">{{desc}}</p>
<p class="location">{{getAddress}}</p>
<p class="dates">
<span class="glyphicon glyphicon-time" aria-hidden="true"></span>
{{formatDate startDate}} bis {{formatDate endDate}}
</p>
</div>
{{/each}}
Template.showTermine.helpers({
getAddress: function() {
var locArray = Termine.findOne({
"title": this.title
}, {
fields: {
locations: 1
}
});
latlngArray = locArray.locations.toString();
var latlong = latlngArray.split(",");
var lat = latlong[0];
var lng = latlong[1];
reverseGeocode.getLocation(lat, lng, function(location) {
Session.set('location', reverseGeocode.getAddrStr());
})
// commented out to prevent infinite loop
//return Session.get('location');
}
});
this is because a Reactive variable (like a Session variable) will cause the whole function it is included in to re-run each time it is changed.
So here you have the Session.set() in the same function as the Session.get(), so it will re-run the getAddress function each time Session.set() is called, which will re-run the thing in a loop.
Since you're returning the result in the same function, you really don't need a Session variable at all here:
you can simply:
reverseGeocode.getLocation(lat, lng, function(location) {
return reverseGeocode.getAddrStr();
})
if this doesn't work (because you're doing an asynchronous call to .getLocation), then you should do this call somewhere else
The best place to do this would be in the Template.mytemplate.onCreated() event
Template.showTermine.onCreated(function() {
var locArray = Termine.findOne({
"title": this.title
}, {
fields: {
locations: 1
}
});
latlngArray = locArray.locations.toString();
var latlong = latlngArray.split(",");
var lat = latlong[0];
var lng = latlong[1];
reverseGeocode.getLocation(lat, lng, function(location) {
Session.set('location', reverseGeocode.getAddrStr());
})});
Template.showTermine.helpers({
"getAddress": function() {
return Session.get("location");
}
});
The set is called inside the callback which will execute after the helper has returned the get already, this mean when it sets the variable the helper gets invalidated because of the change in the session value and reruns.
Couple of possible fixes:
Move code to set the var into the onCreated or onRendered methods and remove everything bar the return from your helper
Or
Ensure the session var is created as a null value initially and then add an if check to look at the value of the var before attempting to call the location service or set the var.
Reactive vars are defo the right way to go over sessions for repeated templates here to avoid namespace collisions in session and the same advice should work for reactive vars or sessions vars. Just avoid setting and getting anything reactive in the same helper without some checks to see if it is needed

Ember.js template binding performance

We have a page containing a table with 26 rows. Each row will contain either an <input> or <select> element, depending on the data we're binding to. When binding to elements that contain between 5-30 options, it takes a page about 5 seconds to render. If I remove the binding, the page renders in under a second.
Is there a known performance issue when binding to Ember.Select views? Or, could I be doing it incorrectly? I'm using Firefox 22. IE9 is about twice as slow. The CPU is not pegged during this time. I'm using ember 1.0rc6.
Template snippet:
{{#if pa.isPickList}}
{{view Ember.Select viewName="select" contentBinding="pa.options" selectionBinding="pa.selected"}}
{{else}}
{{input valueBinding="pa.selected"}}
{{/if}}
I worry that the async nature of how I'm fetching the model could be causing inefficiencies. Perhaps the binding and async events are interacting inefficiently.
Salesforce.com is the backend. From what little I know about promises, I'm wondering if I need to fetch the server data in a promise. I'm not sure how to do this.
Here's how I'm currently fetching the data in my Route:
App.IndexRoute = Ember.Route.extend({
model: function(params){
var otherController = this.controllerFor('selectedProducts');
var ar = Ember.A(); //create an array
var arg = '0067000000PNWrV';
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.ProductPricingLookupController.loadOpportunityProducts}',
arg,
function myHandler(result, event) {
console.info('got results!!! ' + result.length);
for(var i = 0; i < result.length; i++)
{
var p = result[i];
var sfProd = App.ProductResult.create({content: p});
ar.pushObject(sfProd);
}
},
{escape: false} //some of the names have ampersands!!
);
return ar;
}
}
Thanks in advance for helping a newbie learn the ways of javascript and Ember.
Update
Here is working example of this issue. I have 5 picklists each with 60 options. This take 2-3 seconds to render on my machine. I realize these are decently large numbers but hopefully not unreasonable. Increase the number of picklist elements or options and you can easily hit 5 seconds.
Also, moving my server-model-fetching to a promise did not affect performance.
Andrew
It's a little hard to guess at performance problems like this without looking at it in a profiler. You can try creating a profile in Chrome dev tools to see what method is taking the most amount of time. Or create a jsbin which has the same issue.
One potential issue is that the array that you bind to is being built at the same time when the bindings are connected. This shouldn't be an issue with rc.6. What version of Ember are you on?
Regards to promises, your model hook should return a promise that wraps your async call, like so.
model: function(params) {
var promise = Ember.Deferred.create();
var myHandler = function(result, event) {
var model = Ember.A();
// populate the model
promise.resolve(model)
}
var arg = '0067000000PNWrV';
Visualforce.remoting.Manager.invokeAction(..., myHandler);
return promise;
}
If the bindings were firing too early/often, loading the model in a promise like this would help.
Finally try setting Ember.STRUCTURED_PROFILE to true. This will show you exactly how long the templates are taking to render.
Edit: After the the jsfiddle
I dug into this a little more. This is a known issue with Ember.Select. The default implementation creates SelectOption views for each option inside the select to allow databinding of the option items itself. Creating those many views is what takes the time.
However the typical usage will rarely need binding to the option items only to the whole list itself. And this appears to be the common way to bridge the performance gap.
I found a gist that uses option tags instead of SelectOption views.
Here's your updated jsfiddle. I upped the lists to 10 with 100 items each. The list renders in about 150ms for me now.

jQuery stops working after ajax request that adds fields to a form in Drupal 7

I don't think this is a Drupal-specific question, but more of a general jquery/ajax issue:
Basically, I'm trying to use javascript to add up form fields and display the result in a "subtotal" field within the same form. Everything is working fine until i click the option to add another field (via ajax), which then changes my "subtotal" field to zero, and won't work again until I remove the field.
Here is the function that adds up the fields:
function calculateInvoiceFields(){
var total = 0;
var rate = 0;
var quantity = 0;
var i = 0;
var $ = jQuery;
$("#field-aminvoice-data-values tr").each(function(){
// quantity field number
quantity = $("#edit-field-aminvoice-data-und-"+i+"-field-aminvoice-quantity-und-0-value").val();
// rate field as number
rate = $("#edit-field-aminvoice-data-und-"+i+"-field-aminvoice-rate-und-0-value").val();
if(!isNaN(quantity) && !isNaN(rate)){
total += quantity*rate;
}
i++;
});
return total;
}
And here are the functions that get fired for .ready and .live:
jQuery(document).ready(function(){
var $ = jQuery;
$(".field-type-commerce-price input").val(calculateInvoiceFields());
});
jQuery(function(){
var $ = jQuery;
$(".form-text").live('change', function(){
$(".field-type-commerce-price input").val(calculateInvoiceFields());
});
});
Any ideas would be a big help. Thanks in advance!
I recommend using 'on' for any binding statement. and 'off' for unbinding.
The reason it doesn't work after an AJAX call, is because you need to be watching for that element to be added to the DOM, and an event attached to it after it gets loaded. If you load a new element in, and there is nothing watching for it, it won't add the event watch to that new DOM element.
As below:
function calculateInvoiceFields(){
/*..*/
return total;
}
$(document).ready(function(){
$(".field-type-commerce-price input").val(calculateInvoiceFields());
$("body").on('change', ".form-text", function(){
$(".field-type-commerce-price input").val(calculateInvoiceFields());
});
});
usually it stops working when an error has been thrown. did you check out your javascript console (firefox firebug, or built in for chrome) for any indication of an error?

Re-running .getJSON on .click to return new results(jQuery/AJAX)

This is driving me crazy, but first I would like to apologize. I am very new to javascript/jquery. I did, however, do my best to solve this by searching relentlessly, and to no avail.
Here's my issue: I am implementing a Flickr photo gallery for a client b/c she insists on using it to upload her pictures. To do this I am using a slider gallery I purchased, and some jQuery/AJAX to create all the functions/requests. The slider is a jQuery script that uses specifically structured selectors to create the slideshow. The structure is like this:
<div class="slider">
<div class="slider-item">
<img src="path/to/img.jpg"/>
<img class="thumbnail" src="path/to/img_thumbnail.jpg"/>
<div class="caption">My Caption</div>
<div class="thumbnail-caption">My Thumb Caption</div>
</div>
</div>
The function I setup is like this:
var $currentPage = 1;
var $totalPages;
var $sliderHTML = "<div class='slider'></div>";
var $perPage = 18;
function flickr(){
$(function (){
$($sliderHTML).prependTo("#portfolio");
$.getJSON('http://path/to/flickr?perpage=' + $perPage + '&page=' + $currentPage + '&format=json&jsoncallback=?',
function(data){
$totalPages = Math.ceil(data.photos.total / $perPage);
$.each(data.photos.photo, function(i,item){
// Loop results and grab all variables I need - Examples omitted
// After which, append variables to string
var $sliderItem = '<div class="slider-item"><img height="' + imgHeight + '" src="' + photoURLo + '" alt="' + photoTitle + '" /><img height="75" width="75" class="thumbnail" src="' + photoURLm + '" alt="' + photoTitle + '" /><div class="caption">' + photoTitle + '</div><div class="thumbnailCaption">' + photoTitle + '</div></div>';
// Then append to the main div
$($sliderItem).appendTo(".slider");
});
});
}
// Finally call the function on document.ready
$(document).ready(function(){
flickr();
});
To make sure the slider doesn't load until the flickr function is completed (which was causing screw-ups) I just wrapped it in a function and call it on window.load. which works perfectly. And finally I initialize the slider w/ a window.load event.
$(window).load(aSlider()); // loads the slider script
$(window).load(advSlider()); // initializes the slider
The whole setup works great. Initially.
Here's the issue. I need to call more pictures and this is not working at all! Here's the code snippet in my HTML:
<script type="text/javascript">
$('#moreSlides').click(function (){
if($totalPages == $currentPage){ //checks for last page
$currentPage = 1; //resets to page one
}
else {
$currentPage++; // increments page
}
$('.slider').remove(); //removes entire slider div
flickr();
$(".slider-item").ajaxComplete(aSlider());
advSlider();
});
</script>
One way or another this always fails. I have tried stepping through this click event in the console and what it basically looks like is that it gets to the flickr() function and just passes over it instead of executing the function (which I can see happen if I step through it in the console on load). However, it does eventually execute the function... it creates the div class=slider, and within it, my loading animation, but after that it does not load the slide-items until after it executes the aSlider() function.
I have reproduced what I want to happen in the console manually and it works fine: Increment the $currentPage: check. Remove the .slider div: check. Execute flickr():check. Execute aSlider: check. Initialize advSlider(): check. Everything works perfectly with all the second page images showing up.
Something is wrong with the sequence and I simply do not know what it is. I have tried everything I can find to get the aSlider to wait, but it doesn't work. Please help!
Thanks
I ended up solving this by creating a setTimeout loop (with if statements).
function loadSlides(){
if($totalPages != $currentPage){ // Making sure we're not on the last page, which may not have the exact $perPage number
if($('.slider-item').size() == $perPage){ // See if all slides have loaded by counting number of .slider-item and comparing to $perPage variable
$('#loader').remove(); // Remove loading animation
aSlider(); // Execute Slider code
setTimeout(function(){
advSlider(); // Instantiate slider (this may not be necessary on the setTimeout here, just being safe)
},50);
}
else {
setTimeout(function(){ // If all slides haven't loaded, wait 20ms and retry the function.
loadSlides();
},20);
}
}
else {
if($('.slider-item').size() == $lastPage){ //This is the same thing as about just takes into account $lastPage instead of $perPage
$('#loader').remove();
aSlider();
setTimeout(function(){
advSlider();
},50);
}
else {
setTimeout(function(){
loadSlides();
},20);
}
}
}
This is what I had to do to make this work. I hope it helps someone b/c getJSON can't be loaded synchronously and this seems like a common issue. Although setTimeout is kind of a hack, using this will allow your code to run more quickly than putting in some generic number like some people seem to do:
$(document).ready(function(){
setTimeout(function(){
aSlider();
},1600);
});
Using setTimeout like that is terrible b/c sometimes its way too long and other times it still isn't long enough. So in order to get it to always work correctly, you have to set a terribly high timeout.
Like I said, I'm very new to all this so if anyone has a better way of writing this or doing the same time, feel free to answer!

Resources