style.display changing after Array Sort - javascript

I want to display a simple loading message before a sort, but the display changes from none to block after the sort even though I call the sort after. I even tried using setTimeout (2 seconds) to change the display then call a dummy function to sort the stuff.
function sort(i) {
document.getElementById("loading").style.display = "block";
array.sort(function(a, b) {
return a[i].localeCompare(b[i]);
});
}

Browsers don't rush to repaint the page while JS is busy working (on the assumption that there will probably be other DOM changes that they should batch together for the repaint).
You'll need to free up the event loop to allow a repaint between your two statements.
Move the call to array.sort into a function and call it after a delay (e.g. with setTimeout or requestAnimationFrame).

Related

How to reset js window objects on a condition

Am using nested if to iterate through a list of window attributes and am deleting the window attribute based on a criteria. Since this s a nested loop, the execution time takes .5seconds. We have to make it quicker and bring it down to millisecs. the problem is we have a lot of automation scripts to run our regression tests and since the document object takes to reload during iteration the automation test pack fails to find elements in the html and throws error in most of the cases.
We asked them automation team to leave a thread.sleep or wait between every action they do but since they have more than 200 test scenarios it's difficult for them to add the time delays in every single action so it has come back to the dev to check on improving the performance.
Please suggest best optimal solution.
I tried multiple iterations to check on the execution time but traditional for seems to beat the rest of it.
Snippet:
function resetWindow(){
const ALL_WIN_KEYS = Object.keys(window);
for(let i=0;i<ALL_WIN_KEYS.length;i++){
let matchFound = false;
for(let j=0;j<DEFAULT_WIN_KEYS.length;j++){
if(ALL_WIN_KEYS[i] == DEFAULT_WIN_KEYS[j]){
matchFound = true;
break;
}
}
if(!matchFound){
delete window[ALL_WIN_KEYS[i]];
}
}
}
Note: DEFAULT_WIN_KEYS is a const declared globally.

Page does not update on setTimeout? [duplicate]

This question already has answers here:
Calling functions with setTimeout()
(6 answers)
Closed 6 years ago.
I wrote a script that will read in a text file and attempt to find duplicate entries in the dataset with the user-entered parameters. As the dataset is quite large, the page tends to freeze. I referenced this post here:
Prevent long running javascript from locking up browser
To perform my calculations in chunks using setTimeout(). I am trying to get a percentage complete to display on the page, but it won't display until the script finishes running (both Chrome and Firefox).
This is my pump function:
function pump(initDataset, dataset, output, chunk){
chunk = calculateChunk(initDataset, dataset, output, chunk);
document.getElementById("d_notif_container").style.display="block";
document.getElementById("d_percent").innerHTML = NOTIF_SEARCH_START + percentComplete + "%"; //This is a div
document.getElementById("i_pc").value = percentComplete; //This is an input text field
if(percentComplete < 100){
console.log("Running on chunk " + chunk);
setTimeout(pump(initDataset, dataset, output, chunk), TIMEOUT_DELAY);
}
else {
comparisonComplete(output);
}
}
I attempted to display in two different ways, using innerHTML and value (two different elements). The percentComplete is a global variable that is updated in calculateChunk().
The strangest thing is, if I inspect one of those elements, I can watch the HTML change counting the percent (in Chrome). It just simply does not show on the actual page until the script is done running. I've tried changing the timeout up to 1000 as well with not luck.
Any idea why this is happening?
This code
setTimeout(pump(initDataset, dataset, output, chunk), TIMEOUT_DELAY);
calls pump and passes its return value into setTimeout, exactly the way foo(bar()) calls bar and passes its return value into foo.
If you want to call pump after a timeout with those values, you can use Function#bind to do it:
setTimeout(pump.bind(null, initDataset, dataset, output, chunk), TIMEOUT_DELAY);
// ------------^^^^^^^^^^
Function#bind returns a new function that, when called, will use the given this value (the first argument) and arguments you give bind.

How to not add a delay to a function

I am new to JavaScript and following this tutorial I have made the game work perfectly and it goes up to the part of were the the level changes when you destroy all the objects. However as I am learning I am trying to figure out how to make it so that it changes level without a delay.
The main part of the bit which switches level is :
if (!this.rockmodel.countLiving()) {
Asteroid.time.events.add(Phaser.Timer.SECOND * gameWindow.delayToStartLevel, this.levelIncrease, this);
}
However if I take out the delayToStartLevel bit, it does not switch level. So I tried to make it looks like this:
Asteroid.time.events.add(this.levelIncrease, this);
But the next level does not show at all. Not sure if I am being an idiot etc, but any help on this matter would be great.
Again just to make some sense, it works fine with the delay, I want to get rid of that function completely but its not working at all.
Thanks.
The time.events.add will add an event to the Phaser game object. In other words it will fire the given function after X milliseconds.
If you do not want a delay then you can just call the function directly, instead of postponing the function call. Something like this:
if (!this.rockmodel.countLiving()) {
this.levelIncrease();
}

slow down execution of javascript's eval

I have created a little robot like the Karel robot (Wikipedia) which is based on javascript.
Karel4Web
The robot can be controlled with some simple commands such as "forward", "turnright" and so on.
The user can write a javascript program to control the robot which then goes through javascripts "eval()" function so that the robot moves.
The problem is that I want the robot to move slowly so that you can see what he is doing and so that you can highlight the current code line in the editor.
Current method: Parsing
At the moment I have solved this (in the offline version) by parsing each line in the textarea and then building a stack of action which are then executed one after another with window.setTimeout. But this is of course limited because I have to write parsing code for every little javascript language contruct which is much work and error prone.
Some additional information to this:
Parsing version: http://abi-physik.de/_niki2/niki.php
Parsing version js code: http://abi-physik.de/_niki2/js/niki.js
Important functions are at the bottom of the script: run(), execute()
I am currently parsing the user script line by line and adding the actions to a stack. If the parser encounters an "if" it will begin a new stack and add all actions to that stack. if the parser then encounters an "}" it will close the "if" stack and continue to add actions to the base stack.
Any idea to improve this?
I would say have those functions register to some queue instead of having them execute the JavaScript directly.
var moveQueue = [];
function forward(){
moveQueue.push(_forward);
}
function _forward(){
alert("move forward");
}
function backward(){
moveQueue.push(_backward);
}
function _backward(){
alert("move backward");
}
Than when it runs you would use a setTimeout and
function run(){
var curStep = 0;
function go(){
moveQueue[curStep]();
curStep++;
if(curStep<moveQueue.length){
window.setTimeout(go,500);
}
}
}
You still would need to parse it out to figure out the if statement logic, but this is one of many ways that will allow you to control the speed of execution.
Javascript doesn't have a sleep() function, so yes, using setTimeout or setInterval is the way to go.
You could parse the 'instructions' first, assemble an array of actions that need to be carried out, then use setInterval to arrange for a function to be regularly called which takes the next instruction and carries it out (or clears the interval, if there are no more instructions waiting to be processed).

Whats happening? One day its OK, the next day its 'undefined'?

I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.

Categories