Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append items ordering by placed amount

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?

like image 479
Sandra Avatar asked Nov 01 '17 20:11

Sandra


2 Answers

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.

like image 56
dhilt Avatar answered Sep 27 '22 23:09

dhilt


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.

like image 23
nipunasudha Avatar answered Sep 27 '22 22:09

nipunasudha