We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.

Tom McKearney • 13 years ago

Why not just use "push" with many arguments?

The observableArray just passes the "arguments" to the Array.push() method.

Array.push() supports many items. So, you could just call push with all the items you want to add in one call like:

self.items.push(data);

Ryan Niemeyer • 13 years ago

@Tom - I can see your point. However, you can't actually just do "self.items.push(data);" though or it will just add the new array as a single additional item of the current array. You would have to do:

self.items.push.apply(self.items, data);

This way each item in the new array is treated as an argument. This is what "ko.utils.arrayPushAll" tries to do if the first arg is an array.

I mainly wanted to illustrate the advantage to operating on the underlying array and push seems to be a spot where I have seen this perf issue crop up. I did update the simplified version to show using push with multiple args via apply.

Thanks!

Kamran A • 13 years ago

I've made two helper methods that attach to observableArrays based on a discussion in the group. Would these handle this performance issue correctly?

http://jsfiddle.net/kamrana...

Ryan Niemeyer • 13 years ago

@Kamran - Yes, you are only performing a single operation on the observableArray, so there will only be one notification. I think that you could possibly simplify your implementations though down to this: http://jsfiddle.net/rniemey.... It takes advantage of the fact that push and unshift (push at the beginning) can operate on multiple arguments.

Damien • 13 years ago

Hi Ryan - thanks heaps for this! A couple of months ago my app stopped working in IE < 9, constantly raising the "this script is taking too long" warning. Try as I might, I couldn't find the problem (which is another issue in itself) and was resigned to the idea that the app would have to be only for 'modern' browsers. It turns out that the issue was due to my passing an observable array to the Underscore JS _.filter function. Adding a simple .items has completely fixed the issue.

cheers :-)

DS

Vinh Tran • 13 years ago

Hi Ryan,

I have a question regarding knockout js performance which is not related to this post. Is there any impact on the performace if I declare all the attributes of a model as observable.

I have page where it simply show the data and page where user would edit the data. However I want to reuse the same model class.

Thanks

Ryan Niemeyer • 13 years ago

@Vinh- In general, the overhead is pretty low. It is a function with some internal state and does require a function call to set/get the value. Many people are happy and successful with simply using the mapping plugin with their data, which converts all of the model into observables and observableArrays. If you have a single model that you want to use in both read-only and editable scenarios, then I would certainly start that way.

If you are dealing with hundreds of instances of this model, then you might want to consider an approach where you share the same model, but provide an extra function to convert all of the relevant properties to observables in the edit scenario.

Vinh Tran • 13 years ago

Thanks for the very helpful reply Ryan

Galeto • 13 years ago

Hello!

Another question regarding performances: If you load a regular html-with-data-attributes template via AMD, add it to the DOM then call applyBindings(model, dom), doesn't it end up being processed/rendered by the browser twice ? (once as the initial raw template, and a second time once knockout finished to process it?) Is there any way to supress this overhead ?

Ryan Niemeyer • 13 years ago

@Galeto - If you have complicated HTML, then yes I suppose it could be some overhead. One way to avoid this overhead would be to use named templates where your HTML lives in a script tag with a type that is somthing other than "text/javascript" like "text/html". Then, it will be ignored until Knockout interacts with it. Named templates work fine with KO 2.0 native templating.

Jaco Jansen • 13 years ago

Hi Ryan, I hope this isn't too far off topic. Found the following and was wondering if you could do a comment about it? I'm looking for a way to update certain properties of the items in an observable array. But I have to reload all my data, or just the object as below. What's the best way to update properties of items in an array (using mapping plugin) without making them observable? Or, what is the best way to make them observable?

There's probably alot of people using the observableArray to store

full objects, and then have som ajax function requesting updates for

the initial dataset.

A sort of generic solutions would be to add something like:

result.update = function (newobject, oldobject) {
        var underlyingArray = result();
        var i = ko.utils.arrayIndexOf(underlyingArray, oldobject);
        if (i != -1) {
            underlyingArray[i] = newobject;
            result.valueHasMutated();
        }

};

to the observableArray function of ko.

This would update the object without having to explicitly set

observable properties for each object in the observable array.

I haven't had time to investigate any drawbacks or pitfalls of this

yet.

Ryan Niemeyer • 13 years ago

Jaco Jansen I think that this would work fine for scenarios where your UI does not need to react to changes to the properties of the item itself.   Replacing the existing item with a new item and calling valueHasMutated.  If you know the index you can even do:  result.splice(index, 1, newItem);  This would simplify it a bit.

Rafael • 13 years ago

Hy Ryan, great posts (the #1 also).

My performance problem comes from the fact that I'm executing all the data-binds even for initially invisible portions of my page. I trying to change this on my logic, but maybe a performance improvement for knockout would be checking if an element (or any parent element) is visible on the UI before performing the update. For example, I have a div, with a foreach bind on an array. But that div is initially invisible, so it does not matter if I change the array, the UI won't have any change (at least, not for the user). Maybe the databind subscribers processing (at least the ones UI related) should check first if they are in a "visible" control hierarchy. Just an idea, what do you think? I could try to implement in a fork, and it could be configurable (the developer could choose the normal or the "visibility aware" mode). 

Rafael • 13 years ago

Nice post. The #1 also.

Ross • 13 years ago

One thing to keep in mind with clearing an observableArray: If you pass in an your own array to the observableArray and rely on the original array to be in sync, setting the observableArray to a new array will cause the original array to stop being updated. It seems obvious, but just something to make a note of.

ranu mandan • 13 years ago

this should be the part of Official Knockoutjs documentation. great article.

Cleyton Ferrari • 13 years ago

Thank you, thank you so much! you saved our project here, we have a screen system that loads combobox 19 (ObservableArray) he fought for about 8 seconds, now with this technique, it opens in less than 1 second! THANK YOU, thank you so much!
This should be included in the official documentation!

ENEYSolutions • 12 years ago

Hope this will help

rhapsodyn • 12 years ago

this.addNewDataBad = function(newData) { //not data
var item;
for (var i = 0, j = newData.length; i < j; i++) {
item = newData[i];
self.items.push(new Item(item.name, item.priority));
}
};

Ryan Niemeyer • 12 years ago

Fixed

Abdel Hady • 12 years ago

Your words here saved me A LOT, so thank you very much,

but I still have a performance hit wanted to ask you about:

I'm viewing something like facebooks newsfeed, but applying the pushing into the observable array still takes a lot of time (from 600 to 1300 millisecond), here is the code of pushing:

self.pushPosts= function(postsArray) {
if(postsArray.length > 0) {
var pulseModelsArray = [];
for (i=0;i<postsarray.length;i++){ pulsemodelsarray.push(new="" pulsemodel(postsarray[i],="" true));="" }="" this._pulses.push.apply(this._pulses,="" pulsemodelsarray);="" }="" };="" kindly,="" do="" you="" have="" any="" idea="" how="" to="" minimize="" this?="">

Abdel Hady Muhammad • 12 years ago

Sorry, the performance hit is in this line:

this._pulses.push.apply(this._pulses, pulseModelsArray);

Ryan Niemeyer • 12 years ago

Abdel Hady Muhammad - hello! It is hard to tell from the snippets. How many items are in the array approximately and how large is each object? Maybe you could put something in jsFiddle that shows the issue? Thanks!

Bill Schneider • 12 years ago

Great stuff. How do you troubleshoot to figure out exactly where your bottleneck is?

Ryan Niemeyer • 12 years ago

Bill Schneider - there are many potential sources. One good thing to do is look for computeds or even observables that are being updated more often than you expect. You can do a manual subscription to do some logging or log in the computed function. A bit of this is described here: http://www.knockmeout.net/2.... Another quick tip to go with that is to see if certain areas are being re-rendered more than you expect. One way is to log a Date in the area that you are concerned about (data-bind="text: name() + ' ' + Date()") like http://jsfiddle.net/rniemey.... If you have any specific scenarios/samples that you are having an issue with, let me know, and I can try to take a look.

sirthomas • 12 years ago

Regarding the last portion about clearing the array - when I tried this, I could see the content flicker in the browser (chrome). The notification of the clear appears to triggering the html update. Can this be avoided?

Ryan Niemeyer • 12 years ago

sirthomas - do you have a sample maybe in jsFiddle? If all of the content is cleared, then KO will remove the corresponding elements. Are you replacing it with a new array?

JStrandelid • 11 years ago

Vill this cause the HTML for all items(including the existing) to rerender or will it only rerender the added or removed?

var arr = this.items();
for(var i=0; i<100; i++)
arr.push(new Item());
this.items(arr);

<div data-bind="foreach: items"> ...</div>

LM • 10 years ago

What about when we want update only several items from observable array? Simply calling

this.otherItems(newData);

doesn't seem to be a good solution since the user may have some active state on the page, like a text selected or something like that. I guess the only solution here is to iterate through all items in the array...

Ganesh • 10 years ago

You saved my day, thank you

King Wilder • 10 years ago

Hi Ryan, is there a way of making the "ko.observableArray.fn.push" method generic? It doesn't seem possible to map the items to a model unless it's hard coded in the function. Is this possible so I can use it for any array? In other words, I might want to push a Contacts array into a Contact model, or a Products array into a Product model, etc. I think you get the idea.

Ryan Niemeyer • 10 years ago

King Wilder there are some different ways that you can do it, but creating an extension might be a good choice. Here are some docs: http://knockoutjs.com/docum.... An extension for what you want might look like: http://jsfiddle.net/rniemey... like:

ko.observableArray.fn.pushNew = function(Constructor) {
// all of the arguments after the Constructor function that we want to use
var items = Array.prototype.slice.call(arguments, 1);

// send each one through the Constructor
var mappedItems = ko.utils.arrayMap(items, function(item) {
return new Constructor(item);
});

// call push with our now modified arguments
return this.push.apply(this, mappedItems);
};

King Wilder • 10 years ago

Ah, that's pretty cool! Nice.

Steve Fortner • 9 years ago

Ryan, this has been a source of confusion for me since version 3.00 came out with the new rateLimit option. I forked your jsfiddle here http://jsfiddle.net/sfortne... and applied ".extend({ rateLimit: 0 });" to the items observable array and now when I press the "Add New Bad" button, I only get a Reevaluation count of 1, not 5. Is pushing to a KO observable array now okay with the rateLimit option applied?

Vijay Thirugnanam • 8 years ago

Great, I wrote about it a long time back when I had performance problem with observableArray: https://vijayt.com/post/per....