Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout.js consuming too much memory

I keep Process Explorer open and inspect the "Private Bytes" column of the firefox.exe process. After pressing the "Add" button in this example:

<script id="tmplComment" type="text/x-jquery-tmpl">
    <div>
        <span>Comment:&nbsp;</span>
        <span data-bind="text: $data"></span>
    </div>
</script>    

<input type="button" id="btnAdd" value="Add"/>
<div id="Content" data-bind="template: {name: 'tmplComment', foreach: Comments}">        
</div>

With this code:

var vm = {Comments: ko.observableArray(["a", "b"])};
ko.applyBindings(vm);
$("#btnAdd").click(function()
{
    for(var i = 0; i<500; i++)
        vm.Comments.push(i.toString());
});

(also see this jsfiddle)

I experience that the private bytes taken by Firefox has increased about 50-100 MByte.

The execution time is also rather long when I compare it to implementations lacking dependency tracking, given this example:

<script id="tmplComment" type="text/x-jquery-tmpl">
    <div>
        <span>Comment:&nbsp;</span>
        <span data-bind="text: $data"></span>
    </div>
</script>    

<input type="button" id="btnAdd" value="Add"/>
<div id="Content" data-bind="template: {name: 'tmplComment', foreach: Comments}">        
</div>

With this code:

var vm = {Comments: ko.observableArray(["a", "b"])};
ko.applyBindings(vm);
$("#btnAdd").click(function()
{
    for(var i = 0; i<500; i++)
        vm.Comments.push(i.toString());
});

(also see this jsfiddle)

My question: Is poor performance inherent when using Knockout.js or am I doing something wrong?

like image 724
Attila Kun Avatar asked Jun 27 '11 20:06

Attila Kun


1 Answers

Setting the memory issue aside for a moment, the majority of the time in your current example would be spent in the foreach option of the template binding. It does quite a bit of work to determine which items in the array were changed to determine how to efficiently add/remove elements from the DOM. In your case, this work is being done 500 times.

You can get better performance, by writing it like:

$("#btnAdd").click(function()
{
    var items = vm.Comments();
    for(var i = 0; i<500; i++) {
         items.push(i.toString());
    }

    vm.Comments.valueHasMutated();
});

This just pushes items to the underlying array without notifying any subscribers until the end (calling push on the observableArray will push the item to the underlying array and call valueHasMutated).

The memory usage on the original sample does seem unusually high. I think that there may be some possible optimizations in the foreach logic that could help, but that would/will take quite a bit more research.

like image 95
RP Niemeyer Avatar answered Sep 23 '22 04:09

RP Niemeyer