fnRowCallback slow when used in bulk
fnRowCallback slow when used in bulk
Code works fine, but it's causing the browser to lock up for a second or so until it finishes executing. Trying to figure out a way to optimize fnRowCallback so it doesn't affect browser as much. Feedback would be appreciated.
Keep in mind; it's fine when executed on 5 rows or less- it's when it goes into bulk that it lags the browser considerably. By bulk, I mean 30 or more.
Code:
[code] function processThis(row) {
addClassToStringContainer('away', 'qsAway', row, 1, 6);
addClassToStringContainer('available', 'qsAvailable', row, 1, 6);
addClassToStringContainer('in chat', 'qsInChat', row, 1, 6);
addClassToStringContainer('lobby', 'qsLobby', row, 1, 6);
addClassToStringContainer('teaming', 'qsTeaming', row, 1, 6);
addClassToStringContainer('in game', 'qsInGame', row, 1, 6);
addClassToStringContainer('after game', 'qsAfterGame', row, 1, 6);
}
function addClassToStringContainer(stringID, classID, row, whichCol, maxCol){
var l = 0;
var hascenter = false;
$(row).children().each(function(index, td){
if(l == whichCol){
if ($(td).html().indexOf(stringID) === 0) {
hascenter = $(td).hasClass('center');
$(td).removeClass();
$(td).addClass(classID);
if(hascenter) $(td).addClass('center');
}
}
l++;
if(l == maxCol) l == 0;
});
}
$(document).ready(function() {
$('#activequeue').html( '' );
$('#theactivequeue').dataTable( {
"fnRowCallback": function( nRow, aData, iDisplayIndex){ processThis(nRow); return nRow; },
"bPaginate": false,
"bJQueryUI": true,
"aaSorting": [[ 1, "desc" ]],
"aoColumns": [
{ "sTitle": "Gamer" },
{ "sTitle": "Status", "sClass": "center" },
{ "sTitle": "In State", "sClass": "center"},
{ "sTitle": "Active", "sClass": "center" },
{ "sTitle": "Away", "sClass": "center" },
{ "sTitle": "Offline", "sClass": "center" }
]
} );
}[/code]
Keep in mind; it's fine when executed on 5 rows or less- it's when it goes into bulk that it lags the browser considerably. By bulk, I mean 30 or more.
Code:
[code] function processThis(row) {
addClassToStringContainer('away', 'qsAway', row, 1, 6);
addClassToStringContainer('available', 'qsAvailable', row, 1, 6);
addClassToStringContainer('in chat', 'qsInChat', row, 1, 6);
addClassToStringContainer('lobby', 'qsLobby', row, 1, 6);
addClassToStringContainer('teaming', 'qsTeaming', row, 1, 6);
addClassToStringContainer('in game', 'qsInGame', row, 1, 6);
addClassToStringContainer('after game', 'qsAfterGame', row, 1, 6);
}
function addClassToStringContainer(stringID, classID, row, whichCol, maxCol){
var l = 0;
var hascenter = false;
$(row).children().each(function(index, td){
if(l == whichCol){
if ($(td).html().indexOf(stringID) === 0) {
hascenter = $(td).hasClass('center');
$(td).removeClass();
$(td).addClass(classID);
if(hascenter) $(td).addClass('center');
}
}
l++;
if(l == maxCol) l == 0;
});
}
$(document).ready(function() {
$('#activequeue').html( '' );
$('#theactivequeue').dataTable( {
"fnRowCallback": function( nRow, aData, iDisplayIndex){ processThis(nRow); return nRow; },
"bPaginate": false,
"bJQueryUI": true,
"aaSorting": [[ 1, "desc" ]],
"aoColumns": [
{ "sTitle": "Gamer" },
{ "sTitle": "Status", "sClass": "center" },
{ "sTitle": "In State", "sClass": "center"},
{ "sTitle": "Active", "sClass": "center" },
{ "sTitle": "Away", "sClass": "center" },
{ "sTitle": "Offline", "sClass": "center" }
]
} );
}[/code]
This discussion has been closed.
Replies
Allan
Query server for JSON data
Check to see if a player in the JSON data exists in the table
--if so, update row (this fires fnRowCallback, and is needed especially since status will be changing with the update)
--if not, add player to table (this fires fnRowCallback, and is needed to modify class based on their status)
Check to see if a player in the table exists in the JSON data
--if so, skip and continue (already handled the player in previous update)
--if not, remove row containing player (doesn't fire fnRowCallback, I think-- correct me if I'm wrong.)
Right now, I'm staring at the processThis() function and the addClassToStringContainer() function. There has to be a better way to handle those two functions.
Think about it. Look at the addClassToStringContainer function-- it basically crawls through the rows. Let's say we have 40 rows. That's 40 loops right there. But wait, we're forgetting that the loop also checks TD by TD. There's 6 columns. That comes out to 240 loops right there already!
Now take a look at the processThis() function; it's executing the addClassToStringContainer() function 7 times.
Which means every time JSON data is obtained (every 5 seconds), it slams the browser with 1680 loops!
The end result? The browser locks up during those loops.
There has to be a way to optimize the code so it doesn't need to run the addClassToStringContainer() seven times, but still retain reusability and flexibility... I can merge the two functions, it'd reduce it so it runs once instead of 7 times for each fnRowCallback. But if I do that, it kills flexibility (can't check a different column, can't check for different type, etc with minimal code).
Got an idea on how to reduce intensivity of fnRowCallback?
Edit: is there a way to have:
$(row).children().each(function(index, td)
work with a specific column? I tried:
$(row).children(1).each(function(index, td)
to select the second column to work with, but it didn't work. If I can have it select a specific column, it'll cut down alot on looping.
[code]var td = $(row).children().eq(whichCol)
// manipulate td here[/code]
or using a drawCallback
[code]// filter added to only process current page of data
// nth-child starts from 1
$(oTable.fnGetNodes()).filter(':visible').find('td:nth-child(' + (whichCol + 1) + ')').each(function(index, td){
// manipulate td's here
})[/code]
[code] var classArray = {
'string2class': [
{
'theString': 'away',
'theClass': 'qsAway'
},
{
'theString': 'available',
'theClass': 'qsAvailable'
},
{
'theString': 'in chat',
'theClass': 'qsInChat'
},
{
'theString': 'reserved',
'theClass': 'qsReserved'
},
{
'theString': 'teaming',
'theClass': 'qsTeaming'
},
{
'theString': 'in game',
'theClass': 'qsInGame'
},
{
'theString': 'after call',
'theClass': 'qsAfterCall'
}
]
};
function processThis(row) {
var hascenter = false, td, cellString, i;
td = $(row).children().eq(1);
// manipulate td here
hascenter = $(td).hasClass('center');
$(td).removeClass();
cellString = $(td).text();
classArray.string2class.length;
for(i = 0; i < classArray.string2class.length; i++){
if (cellString == classArray.string2class[i].theString) { $(td).addClass(classArray.string2class[i].theClass); }
}
if(hascenter){ $(td).addClass('center'); }
}[/code]
The page is working much better; the lockup is considerably slower-- it only locks up for a second. But the lockup still exists when loading large amounts of data.
So I'd like to convert the above code to use drawcallback instead. Suggestion?
Edit: Is there a way to track bottlenecks in the code? I'm going to hit Google for this, but would like to hear your solution for tracking down bottlenecks.
By the way, thanks for your assistance!
The end result? Well formatted table, and the lock up pushed down to less than a second. The lockup's still there, but it's barely noticeable-- only way to actually catch it is if you're scrolling and you'll see it lock up for less than a second.
I ran the profiler again, and this time DataTables was no longer what was locking it up. It's now:
addClass
removeClass
So now it's jQuery that is causing it to lag alot. That's an issue that doesn't belong in this forum, so I'd like to thank everyone for your assistance. Of course, if you have an idea how to optimize the above code so those two commands are not as intensive, I'm open to it!
Allan