Problems with Backbone jQuery loading order - javascript

In one of my views I am pulling in a collection (user_id, date, # of posts, avg post length) and then performing some math calculations on the data (ex: aggregate the total number and avg post length.
To do this I am looping through using an underscore each function and creating a new view (element is a tr) that is appended to a table.
At the end of the loop I am calling the jquery sort table plugin to sort the data in the table. The problem I'm running into is that a lot of times the each loop hasn't finished running before I call the jQuery sort table. I have set it up so there is a delay before the jquery is called, but what is a more elegant solution that will work 100% of the time in this scenario.
var size = _.size(userposts);
_.each(userposts, function(post) {
var sum = 0;
var totposts = 0;
j++;
_.each(post, function(times) {
sum += (times.get("posts") * times.get("avgpost"));
totposts += times.get("posts");
});
if(post[0].get("userId")){
var tempuser = new User({id: post[0].get("userId")});
tempuser.fetch({ success: function() {
$("#user-posts").append(new UserPostView({data: {name: tempuser.get("name"), num: totposts, avg: parseInt((sum / totposts))}}).render().el);
}});
}
if (j == size) {
formatTable();
}
});

So, perhaps I am missing your questions, but to lay it all out, you have:
a loop (or a collection of loops)
an event that needs to fire at the end of the loop?
A timer is a big no no! What if this process takes longer on other computers? Or a mobile device? You never want to rely on timers to queue up function calls that rely on pre existing data, especially not without a backup.
There are 2 ways I can think of doing this
var loopStatus = 0;
for (var i = 0; i < n; i++){
someMethod();
loopStatus++
if (loopStatus = n){
someMethod2();
}
}
or you could just use a while with the someMethod2() call at the end of it, outside the loop

Related

Iterate over a Range fast in Excelscript for web

I want to check that a range of cell are empty or has any values in them, I use this for loop :
for (let i = 0; i <= namesRange.getCellCount(); i++) {
if (namesRange.getCell(i,0).getText() == "")
{
break;
}
bookedCount += 1;
}
However this iteration is extremely slow (as is the use of Range.getValue, but the console warns you that iterating with .getValue is slow, does not warn you with getText) It takes several seconds to iterate over a very short list of 10 elements.
Is there any way to check for the values of a cell in a speedy manner using ExcelScripts?
Does this mean that, even if I develop a UDF or a ribbon Add-In with office.js and Node.js it will also be this extremely slow for iterating over cells?
Is there any way to make this faster?
The reason your code is likely performing slowly is that the calls to getCell() and getText() are expensive. Instead of performing these calls every time in the loop you can try a different approach. One approach is to get an array of the cell values and iterate over that. You can use your namesRange variable to get the array of values. And you can also use it to get the row count and the column count for the range. Using this information, you should be able to write nested for loops to iterate over the array. Here's an example of how you might do that:
function main(workbook: ExcelScript.Workbook) {
let namesRange: ExcelScript.Range = workbook.getActiveWorksheet().getRange("A1");
let rowCount: number = namesRange.getRowCount();
let colCount: number = namesRange.getColumnCount();
let vals: string[][] = namesRange.getValues() as string[][];
for (let i = 0; i < rowCount; i++) {
for (let j = 0; j < colCount; j++) {
if (vals[i][j] == "") {
//additional code here
}
}
}
}
Another alternative to the first answer is to use the forEach approach for every cell in the range of values.
It can cut down the amount of variables you need to achieve the desired result.
function main(workbook: ExcelScript.Workbook)
{
let worksheet = workbook.getActiveWorksheet();
let usedRange = worksheet.getUsedRange().getValues();
usedRange.forEach(row => {
row.forEach(cellValue => {
console.log(cellValue);
});
});
}

Creating a for loop that loops over and over =

So I have a weird problem (as I can do this using dummy code, but cannot make it work in my actual code) -
The concept is simple - I need a for loop that upon hitting its max "I" number reverts "I" to 0 again and creates a loop over and over -
DUMMY CODE:
for(i=0;i<10;i++){
console.log(i);
if(i === 10){
i = 0
}
}
Now for the longer code (sorry)
function reviewF(){
// add ID to each of the objects
reviews.forEach((e, i)=>{
e.id = i
})
// get the elements to be populated on page
var name = document.querySelector('p.name');
var date = document.querySelector('p.date');
var rating = document.querySelector('.rating_stars');
var review = document.querySelector('p.review_content_text');
// reverse the array - so the newest reviews are shown first (this is due to how the reviews where downloaded)
var reviewBack = reviews.slice(0).reverse();
// start the loop - go over each array - take its details and apply it to the elements
/**
* THIS IS WHAT I WOULD LIKE TO LOOP OVER FOREVER
*
* **/
for (let i = 0; i < reviewBack.length; i++) {
(function(index) {
setTimeout(function() {
// document.getElementById('reviews').classList.remove('slideOut')
name.classList.remove('slideOut')
date.classList.remove('slideOut')
rating.classList.remove('slideOut')
review.classList.remove('slideOut')
name.classList.add('slideIn')
date.classList.add('slideIn')
rating.classList.add('slideIn')
review.classList.add('slideIn')
name.innerHTML = reviewBack[i].aditional_info_name;
date.innerHTML = reviewBack[i].Date;
rating.innerHTML = '';
review.innerHTML = reviewBack[i].aditional_info_short_testimonial;
if(reviewBack[i].aditional_info_short_testimonial === 'none'){
reviewBack.innerHTML='';
}
var numberOfStars = reviewBack[i].aditional_info_rating;
for(i=0;i<numberOfStars;i++){
var star = document.createElement('p');
star.className="stars";
rating.appendChild(star);
}
setTimeout(function(){
// document.getElementById('reviews').classList.add('slideOut')
name.classList.add('slideOut')
date.classList.add('slideOut')
rating.classList.add('slideOut')
review.classList.add('slideOut')
},9600)
}, i * 10000)
})(i);
// should create a infinite loop
}
console.log('Loop A')
}
// both functions are running as they should but the time out function for the delay of the transition is not?
reviewF();
EDITS >>>>>>>>
Ok so I have found a hack and slash way to fix the issue - but its not dry code and not good code but it works.....
this might make the desiered effect easier to understand
reviewF(); // <<< this is the init function
// this init2 function for the reviews waits until the reviews have run then
// calls it again
setTimeout(function(){
reviewF();
}, reviews.length*1000)
// this version of the innit doubles the number of reviews and calls it after that amount of time
setTimeout(function(){
reviewF();
}, (reviews.length*2)*1000)
From trying a bunch of different methods to solve this issue something I noticed was when I placed a console.log('Finished') at the end of the function and called it twice in a row (trying to stack the functions running..... yes I know a horrid and blunt way to try and solve the issue but I had gotten to that point) - it called by console.log's while the function was still running (i.e. the set time out section had not finished) - could this have something to do with it.
My apologies for the rough code.
Any help here would be really great as my own attempts to solve this have fallen short and I believe I might have missed something in how the code runs?
Warm regards,
W
Why not simply nest this for loop inside a do/while?
var looping = True
do {
for(i=0;i<10;i++){
console.log(i);
}
if (someEndCondition) {
looping = False;
}
}
while (looping);
I would think that resetting your loop would be as simple as setting "i = 0" like in the dummy code. So try putting the following into your code at the end of the for loop:
if(i === 10){
i = 0;
}

How to optimize a long running script

In my application a user is able to update multiple rows of my Kendo grid. After that information gets saved in my data base i have to update the rows. But the only way i can do that is to iterate through my partitions and every row of the grid to update these records.
This causes long running script errors and takes forever. and locks up the browser. My question is; is there a way i can optimize my iteration to happen either in another thread or in the background. Or is there a quicker way i can update the records.
//maingrid contains 400 records
//10 partitions containing 10 rows changed by user
//100 records needing to be updated.
function updateAssignmentsInUI(partitions) {
for (var i = 0; i < partitions.length; i++) {
for (var j = 0; j < partitions[i].length; j++) {
var mainGrid = $("#mainGrid").data("kendoGrid");
$.each(mainGrid.dataSource.data(), function () {
if (this.RowSelected === true) {
if (this.ID === partitions[i][j].ID) {
var row = mainGrid.dataSource.getByUid(this.uid);
row.set("Changed", "Yes");
}
}
});
}
}
}
this loops through 10 partitions, then loops through 10 records, the looks for the record in the entire list of 400 records in the maingrid. So you can imagine how long this takes before the user gets control again.
You can just try to change / cache your queries. I don't know this will help much but try to use your code like this
//maingrid contains 400 records
//10 partitions containing 10 rows changed by user
//100 records needing to be updated.
function updateAssignmentsInUI(partitions) {
var mainGrid = $("#mainGrid").data("kendoGrid");
$.each(mainGrid.dataSource.data(), function () {
if (this.RowSelected === true) {
for (var i = 0; i < partitions.length; i++) {
for (var j = 0; j < partitions[i].length; j++) {
if (this.ID === partitions[i][j].ID) {
var row = mainGrid.dataSource.getByUid(this.uid);
row.set("Changed", "Yes");
}
}
}
}
});
}
This way your mainGrid will be cached and you will not query it multiple times and also wont search for data of it, also you will not run expensive n^2 loops if row is not selected.
Here is a working demo (simplified) :
https://plnkr.co/edit/n3ZHmlqtoKZP4UQikw2A?p=preview
For the n^2 loops. I dont know much to optimize it, ask it for more algorithm gurus.

dom referenceing vs storing dom reference [duplicate]

This question already has answers here:
performance issue : storing a reference to DOM element vs using selectors
(4 answers)
Closed 8 years ago.
What is the difference between the following statements?
$("#txt").val("123");
and
var txt=$("#txt");
txt.val("123");
Which one is most efficient?
Both version go through the motion of finding a relevant dom element and setting a value
There is no benefit to doing one or the other unless you need to reference it again. Consider this:
if ( $("#txt").val()!='123' )
$("#txt").val("123");
// vs.
var txt=$("#txt");
if ( txt.val()!='123' )
txt.val("123");
The latter is more efficient because you are only looking for the element once.
But if you are simply setting it.. either way in your example is going to be the same amount of work. Slightly more overhead from setting a variable first, but nothing to shake a stick at...but even the above situation isn't likely worth shaking a stick at either, unless you enjoy nickel and diming fractions of microseconds.
In your code, if you need only once to target a desired element selector:
$("#txt").val("123"); // is just fine
But most of the time programmers need to reference multiple times to the same element, and to increase the overall application performance they store the selector into a variable memory slot:
var txt = $("#txt");
var input = $("#someInput");
input.on('input', function() {
txt.val( this.value ); // *
});
txt.addClass('selected'); // JS says "hey I know this guy!"
where at this point JS doesn't need to go all over the document looking for your #txt input on every keyup / keydown or other input events. Can you imagine the benefit?
If you're going to be operating on the value of #txt more than once, it is best to cache the selector as you did in the second version, if you're going to be using the object multiple times. In the end, you probably won't notice any considerable performance increases by using one method v. the other.
As for quantitative data:
versionOne(): 0.0359999998472631
versionTwo(): 0.02500000037252903
This test seems to suggest that the first one is slower, using window.performance. When running this, make sure to empty your browser cache.
(function() {
var v1 = [];
var v2 = [];
function avg(data) {
var sum = 0
for(var i = 0; i < data.length; i++) {
sum += data[i];
}
return sum / data.length;
}
function versionOne() {
$("#txt").val('hello');
}
function versionTwo() {
var txt=$("#txt");
txt.val("123");
}
for(var i = 0; i < 1000; i++) {
var start = window.performance.now();
versionOne();
var end = window.performance.now();
v1.push(end - start);
var start = window.performance.now();
versionTwo();
var end = window.performance.now();
v2.push(end - start);
}
console.log("versionOne(): " + avg(v1));
console.log("versionTwo(): " + avg(v2));
})();
Fiddle

For loop exiting early while updating modified or new records in datatable.net table

I'm using the Datatables jQuery plugin. The table is pulling the data from an AJAX source (a SQL Server query, processed by ASP.NET into a JSON object). I want to create a live view of the table so that changes appear in real time. But rather than reloading the entire table every few seconds with fnReloadAjax() (which, from experience, has proven to be quite burdensome on the browser) I'm only updating the records that are new or modified, using fnAddData() and fnUpdate().
After getting a JSON object of just the new or modified records, here's my code to process the object.
var newData = updatedDataJSON.aaData;
if (newData[0] != null) {
for (i = 0; i < newData.length; i++) { //Loop through each object
if (newData[i].bNewCase === true) { //Process new cases
oTable.fnAddData(newData[i]);
} else { //Process modified cases
var tableArray = oTable.fnGetData();
var index;
var found = false;
var serial = newData[i].serial;
var dataObject = newData[i];
//First gotta find the index in the main table for
// the record that has been modified. This is done
// by matching the serial number of the newData
// object to the original aData set:
for (ii = 0; ii < tableArray.length; ii++) {
var value = tableArray[ii]['serial'];
value = value.replace(/<\/?[^>]+(>|$)/g, "");
if (value === serial) {
index = ii;
found = true;
}
}
if (found) {
oTable.fnUpdate(dataObject, index);
console.log('Updated ' + newData[i].serial);
}
}
}
}
My problem is that even though the newData.length property of the first for loop could be greater than 1, the for loop exits early (after one iteration). I added the console.log statement at the end and it started passing errors saying that newData[i].serial was undefined. This makes me think that the entire newData array was destroyed or something...
I'm really hoping that I've just made a stupid mistake (though I've checked and checked and checked some more but can't find one). Maybe there's something that I'm overlooking. If anyone has any advice, it would be greatly appreciated.
Credit goes to #elclarnrs for the solution, posted above in the comments. The solution was declaring the values of i and ii in the scope of the function. That got everything working smoothly. Good to know for future reference.

Categories