Search code examples
javascriptsortingknockout.jsobservable

knockout.js sorted and ratelimit not working together well, infinite loop


For some reason my sorted method here in conjunction with ratelimit is causing an endless loop. I am not sure why it won't stop being hit. If I remove the ratelimit or the sorted there is no endless loop, but if I use both it won't stop.

function Unscheduled() {
    var self = this;
  self.games = ko.observableArray([]).extend({
            sorted: function(l, r) {
              return l.length > r.length ? -1 : 1;
            },
            rateLimit: { timeout: 0, method: "notifyWhenChangesStop" }});

    self.changes = ko.observable(0);
    self.games.subscribe(function (changes) {
                console.log('Array Hit');
                self.changes(self.changes()+1);
        }, null, "arrayChange");
        
  self.games.push('test');
  
  self.addRemove = function() {
  self.games.remove('test1');
  self.games.push('test');
  }
}
    ko.extenders.sorted = function (obs, sortFunction) {
        ko.computed(function () {
        console.log('sorting');
            obs.sort(sortFunction)();
        });
    };

ko.applyBindings(new Unscheduled());
<script src="https://exposureevents.com/scripts/knockout-3.5.0.min.js"></script>
<div data-bind="text: changes">

</div>
<button data-bind="click: addRemove">
Add/Remove
</button>


Solution

  • Seems that the issue is related to sort method of observableArray. I guess when using that method it generates a change notification then it tries to sort again and goes in this forever loop. When using sort on "raw" array there is no such behavior:

    function Unscheduled() {
        var self = this;
      self.games = ko.observableArray([]).extend({
                sorted: function(l, r) {
                  // return l.length > r.length ? -1 : 1;
                  return l - r;
                },
                rateLimit: { timeout: 0, method: "notifyWhenChangesStop" }});
    
        self.changes = ko.observable(0);
        self.games.subscribe(function (changes) {
                    console.log('Array Hit');
                    self.changes(self.changes()+1);
            }, null, "arrayChange");
            
      // self.games.push('test');
      self.games.push(0);
      
      self.addRemove = function() {
      // self.games.remove('test1');
      // self.games.push('test');
      self.games.push(Math.random() * 10000 | 0);
      }
      self.gamesText = ko.pureComputed(() => self.games.reversed().join('\n'));
    }
        ko.extenders.sorted = function (obs, sortFunction) {
            ko.computed(function () {
                console.log('sorting');
                // obs.sort(sortFunction)();
                obs().sort(sortFunction);
            });
        };
    
    ko.applyBindings(new Unscheduled());
    <script src="https://exposureevents.com/scripts/knockout-3.5.0.min.js"></script>
    <div data-bind="text: changes">
    </div>
    <button data-bind="click: addRemove">
    Add/Remove
    </button>
    <pre data-bind="text: gamesText"></pre>