We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.
@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!
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...
@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.
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
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
@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.
Thanks for the very helpful reply Ryan
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 ?
@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.
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.
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.
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).
Nice post. The #1 also.
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.
this should be the part of Official Knockoutjs documentation. great article.
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!
Hope this will help
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));
}
};
Fixed
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?="">
Sorry, the performance hit is in this line:
this._pulses.push.apply(this._pulses, pulseModelsArray);
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!
Great stuff. How do you troubleshoot to figure out exactly where your bottleneck is?
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.
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?
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?
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>
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...
You saved my day, thank you
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.
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);
};
Ah, that's pretty cool! Nice.
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?
Great, I wrote about it a long time back when I had performance problem with observableArray: https://vijayt.com/post/per....
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);