I am using Knockout JS, as business requirements dictate that most, if not all logic is processed in the browser due to low bandwidth users. It's working out awesome so far except for one issue.
I am using a number of multiselect dropdown lists that all contain cascading logic. I have, say 8 lists that process hierarchical data and alter selectable options in child lists.
This is all good until I get to the bottom 2 lists which could potentially contain 3000 items depending on parent list selections (especially when 'select all' is clicked).
the problem is, in IE, I'm getting long running script warning messages, which I need to get rid of. Here's some code:
viewModel.BottomLevelList= ko.dependentObservable(function () {
if (this.ParentList().length === 0) { //nothing selected
return [];
}
var result = [];
var i = self.longMasterList.length;
var currentId = 0;
while (i--) {
//psuodo code:
//this.ParentList().Contains(loop-item) then
//put in return list based on some further logic
//else continue
}
return result;
}, viewModel);
I have tried using various setTimeout techniques from SO to break the large array up and return control momentarily to the browser, but with no success. The result is never returned and / or the observable seems to detach itself leaving an empty list in the UI.
If I need to use AJAX I will, but this is a very last resort and would prefer to keep it in the client.
So my question boils down to:
- How can I stop long running script warnings as a result of processing large data sets (in the context of Knockout JS dependant observables and cascading lists)
- Is there some idiomatic JavaScript technique I could / should be using in this scenario
- Am I not seeing the wood for the trees here?!
Thanks muchly for any help
I would first suggest that you optimize your
dependentObservable
. When you read any observable, Knockout registers a dependency to it in Dependency Manager. It contains pretty simple code like this:I can see in your pseudo-code that you are accessing this.ParentList() in the
while
loop. It meansregisterDependency
will be called 3000 times and thedependencies
array will be scanned 3000 times, which is bad for IE (since it has no built-in Array.indexOf method).So my number one suggestion would be: Read all observables before loops.
If it doesn't help, I suggest that you proceed with setTimeout(). It is a bit tricky. Please check out this example: http://jsfiddle.net/romanych/4KGAv/3/
I have defined
asyncObservable
. You should pass an array with all dependencies of your dependentObservable. Whenko.toJS
is called, all observables are unwrapped. Then we call the passed callback function with arguments enumerated in thedependencies
array. This function will be evaluated async.I have wrapped this code into ko.dependentObservable to re-evaluate
loader
callback on any change of passed elements passed independencies
UPDATE: My code was overcomplicated for this issue. throttle extender will do the trick. Please checkout this sample: http://jsfiddle.net/romanych/JNwhb/1/