5

I'm using this function to append new items in order by the amount. This function is being called every 30-50ms.

var insertBefore = false;
container.find('.roll-user-row[data-user-id="' + user_data.id + '"]').remove();

container.children().each(function () {
    var betContainer = $(this), itemAmount = $(this).attr('data-amount'), betId = $(this).attr('data-user-id');
    if (itemAmount < betData.totalAmount) {
        insertBefore = betContainer;
        return false;
    }
});

if (insertBefore) {
    $(template).insertBefore(container);
} else {
    container.prepend(template);
}

itemAmount = $(this).attr('data-amount') is integer, betData.totalAmount is interger too. And if appending goes slower than ±300ms - everything works well. In case of fast appending I get this result:

random order

and thats not even close what I want - thats random. How to solve this?

nipunasudha
  • 2,427
  • 2
  • 21
  • 46
Sandra
  • 315
  • 3
  • 16

2 Answers2

4

1. Refactoring

First of all, return within .each callback doesn't work. It just breaks current iteration, not all the cycle. If you want to interrupt cylce, you should use simple for-loop and break statement. Then, I would recommend to call $() as rarely as possible, because this is expensive. So I would suggest the following refactoring for your function:

function run() {
  container.find('.roll-user-row[data-user-id="' + user_data.id + '"]').remove();

  var children = container.children();
  for (var i = 0; i < children.length; i++) {
    var betContainer = $(children[i]); // to cache children[i] wrapping
    var itemAmount = betContainer.attr('data-amount');
    var betId = betContainer.attr('data-user-id');

    if (itemAmount < betData.totalAmount) {
      $(template).insertBefore(container);
      return; // instead of "break", less code for same logic
    }
  }

  container.prepend(template); // would not be executed in case of insertBefore due to "return"
}

2. Throttling

To run a 50ms repeating process, you are using something like setInterval(run, 50). If you need to be sure, that run is done and this is 300ms delay, then you may use just setInterval(run, 300). But if the process initializes in a way that you can't change, and 50ms is fixed interval for that, then you may protect run calling by lodash throttle or jquery throttle plugin:

var throttledRun = _.throttle(run, 300); // var throttledRun = $.throttle(300, run);
setInterval(throttledRun, 50);

setInterval is just for example, you need to replace your initial run with throttled version (throttledRun) in your repeater initialization logic. This means that run would not be executed until 300ms interval has passed since the previous run execution.

dhilt
  • 18,707
  • 8
  • 70
  • 85
  • It looks like the right answer but I can't mark it correct as it creates queue of waiting appends with throttle. And what I need is real time update as there are 15 seconds to append all elements. After those 15 seconds I would just have to cancel throttle without appending new elements, right? Correct my if I'm wrong. – Sandra Nov 06 '17 at 13:56
  • @Sandra Throttling just skips unwanted calls, it does not create a queue. So we have, say, throttled real time here. You may cancel trailing throttled invokation by `throttledRun.cancel()`. Just call it somewhere you need. Also you may override all the procedure to make it dummy: `run = function() { return null; }` or add a condition for preventive `return` or... There are a lot of ways for any use case, just need to clarify the use case. – dhilt Nov 06 '17 at 15:09
  • so I tried this method and still faced the same issue.. the callbacks are in random time range, between 50 and ±300ms. Thanks for helping! – Sandra Nov 06 '17 at 19:59
  • @Sandra I believe, your issue could be easily fixed and the thing that could help to do it is reproducable demo. [jsfiddle](https://jsfiddle.net), [plunker](https://plnkr.co/edit/) or whatever would be a good option for repro. The problem is somewhere out of the code you provided in the question. – dhilt Nov 07 '17 at 00:24
2

I am only posting the approach here, if my understanding is right, then I'll post a code. First thing came to my mind reading this was the 'Virtual DOM' concept. Here is what you can do,

  1. Use highly frequent random function calls only to maintain a data structure like an object. Don't rely on DOM updates.

  2. Then use a much less frequent setInterval repetitive function call to redraw (or update) your DOM from that data structure.

I am not sure there are any reason you can't take this approach, but this will be the most efficient way to handle DOM in a time critical use-case.

nipunasudha
  • 2,427
  • 2
  • 21
  • 46
  • The "30ms frequent function calls" is not the real thing, I just named it. This is actually socket emit's from backend depending of what happened on server side. And thats why it is 30-50-100ms - on the other hand random. – Sandra Nov 08 '17 at 19:16
  • It doesn't matter, random or not, you can cleanly update a data structure. Then use that to generate the view. – nipunasudha Nov 08 '17 at 19:31