Knockout.js Observable Arrays Performance

A look at knockout observable arrays performance

by vintharas

HTML

<h1>Test the Performance of Observable Arrays with Gandalf!!</h1>
<p>Add ganfalses with the buttons below...</p>
<button data-bind="click: addGandalfsesBad">Add Gandalfses Bad!</button>
<button data-bind="click: addGandalfsesGood">Add Gandalfses Good!</button>
<button data-bind="click: addLotsOfGandalfsesBad">Add Lots Of Gandalfses Bad!</button>
<button data-bind="click: addLotsOfGandalfsesGood">Add Lots Of Gandalfses Good!</button>
<button data-bind="click: clearAll">Clear all</button>

<h2>Results</h2>
<!-- ko foreach: results -->
<article>
    <p>Number of elements in array: <span data-bind="text: $parent.gandalfses().length"></span></p>
    <p>Number of elements added to array: <span data-bind="text: elementsAddedtoArray"></span></p>
    <p>Number of notifications: <span data-bind="text: numberOfNotifications"></span></p>
    <p>Time to add new items and render array: <span data-bind="text: timeToAddNewItems"></span> milliseconds</p>
</article>
<!-- /ko -->
    
<h2>Array of Gandalfses</h2>
<!-- ko foreach: gandalfses -->
<p><span data-bind="text:name"></span>, the <span data-bind="text: color"></span></p>
<!-- /ko -->

CSS

article{
    margin-bottom: 10px;
}

article p{
    margin: 0px 10px;
}

JavaScript

var ViewModel,
    Result,
    moreGandalfses,
    lotsOfGandalfses;


ViewModel = function(){
    var self = this;
        
    self.gandalfses = ko.observableArray([{name: 'Gandalf', color: 'Grey'}]);
    self.numberOfNotifications = ko.observable("");
    // self.results = ko.observableArray([]);
    // I have added this below to illustrate how you can set
    // the context of an observable array explicitely
};

ViewModel.prototype.clearAll = function(){
    var self = this;
    self.gandalfses([]);
    self.results([]);
};

ViewModel.prototype.addGandalfsesBad = function(){
    var self = this,
        newResult = new Result(self.gandalfses);
    
    moreGandalfses.forEach(function(aGandalf){
        self.gandalfses.push(aGandalf);
    });
    newResult.finishedAddingItems();
    self.results.unshift(newResult);
};

ViewModel.prototype.addGandalfsesGood = function(){
    var self = this,
        newResult = new Result(self.gandalfses),
        innerArray = self.gandalfses();
    
    moreGandalfses.forEach(function(aGandalf){
        innerArray.push(aGandalf);
    });
    // explicitely trigger notifications
    self.gandalfses.valueHasMutated();
    
    newResult.finishedAddingItems();
    self.results.unshift(newResult);
};

ViewModel.prototype.addLotsOfGandalfsesBad = function(){
    var self = this,
        newResult = new Result(self.gandalfses);
    
    for (var i = 0; i < 50; i++){
        moreGandalfses.forEach(function(aGandalf){
        self.gandalfses.push(aGandalf);
        });
    }
    newResult.finishedAddingItems();
    self.results.unshift(newResult);
};


ViewModel.prototype.addLotsOfGandalfsesGood = function(){
    var self = this,
        newResult = new Result(self.gandalfses),
        innerArray = self.gandalfses();    
    
    for (var i = 0; i < 50; i++){
        moreGandalfses.forEach(function(aGandalf){
            innerArray.push(aGandalf);
        });
    }
    // explicitely trigger notifications
   ...