Loop not stopping or not starting - javascript

I am making a small userscript for a survey site. Whenever I click the survey and it is indicated that I did not receive it the script is supposed to refresh a certain amount of times trying to claim the survey until it gives up.
(If you do not know what a userscript is, think of it as something that stays running all the time on a specific website, so you can control things using javascript selectors and stuff)
This is my script so far (the javascript portion as that is what the problem is in):
var elementExists = document.getElementsByClassName("message warning")[0];
if(elementExists)
{
var attempts = 0;
while(attempts<5)
{
attempts += 1;
location.reload();
if(elementExists)
{
//nothing
}
else
{
window.stop();
}
}
window.stop();
}
This is actually my first time using Javascript so I assumed that would be the reason for errors, but after 45 minutes of debugging I am completely baffled. If I remove that last "window.stop();" the code refreshes the webpage infinitely. If that stays there then the code doesn't even start. It seems almost as if the while loop is being skipped if the "window.stop();" is present. Is this something that Javascript does, or is the problem elsewhere?
If someone could lead me in the right direction or help me fix this I would be very grateful!
(Also I checked the selector to see if that is the issue, but I have done that correctly)
UPDATE: Turns out location.reload(); stops the script and thus forces a reload. Since I am creating a userscript I realized that I could use the Greasemonkey APIs (or more like stumbled upon). By using GM_setValue and GM_getValue I was able to work around this problem and the script successfully reloaded a certain amount of times (depending on the variable tries) and stopped when it finished. But after messing around a bit, then reverting to the older version the script, the script doesn't doesn't execute at all anymore; "counter < tries" seems to be false for some reason... could anyone figure out why? Also if documentation is needed:
https://wiki.greasespot.net/GM_getValue
https://wiki.greasespot.net/GM_setValue
var tries = 5;
var elementExists = document.getElementsByClassName("message warning")[0];
var counter = GM_getValue('counter', 0);
if(elementExists && counter < tries)
{
GM_setValue('counter', ++counter);
location.reload();
}
(Both counter and tries seem to be integer values.. so there should be in problem in comparing them...)
Also as suggested by #yuriy636 I attempted to reset the variables and created something like this
var tries = 5;
var elementExists = document.getElementsByClassName("message warning")[0];
var counter1 = GM_getValue('counter1', 0);
if(elementExists && counter1 < tries)
{
GM_setValue('counter1', ++counter1);
location.reload();
}
if(elementExists && counter1 == tries)
{
GM_deleteValue('counter1');
window.close();
}
if(!!elementExists)
{
GM_deleteValue('counter1');
return;
alert("stops script while hidden");
}
But again I am hit with the infinite loop.. RIP
Update 2: Not so RIP afterall... solution:
var tries = 50;
var elementExists = document.getElementsByClassName("message warning")[0];
var counter = GM_getValue('counter', 0);
if(elementExists && counter < tries)
{
GM_setValue("counter", counter + 1);
location.reload();
}
else
{
GM_deleteValue("counter");
}
if(elementExists && counter >= tries)
{
window.close();
}
100% Working, after indicated amount of tries, if error still exists the page is closed

The most likely problem is that you have location.reload() in your while loop. This causes the page to refresh before anything interesting happens in your loop. In this particular code I would expect the page to refresh seemingly infinitely because every time the page refreshes, it will refresh again.

Normally this would look more like:
var elements = document.getElementsByClassName("message warning");
for (var i = 0; i < elements.length; i++) {
console.log (elements[i]);
}
.getElementsByClassName returns an array of elements with the class message and/or warning which you are capturing only the 1st one [0]. Hope that helps.

Related

Why/How is my code causing a memory leak?

I have written the following JavaScipt code within a Spotfire TextArea. I include the application and tag for completeness, but I don't believe my issue is Spotfire-specific. Essentially, I have a timer which runs every 5 minutes, and clicks on a link (clickLink('Foo');) to trigger execution of some Python code elsewhere in the application. If the application also contains a timestamp of the last full update, which occurs every 30 minutes in the same manner (clickLink('Foo');):
function reportTimestamp() {
var timeNow = new Date();
var hoursNow = timeNow.getHours();
var minutesNow = timeNow.getMinutes();
var secondsNow = timeNow.getSeconds();
return hoursNow + ":" + minutesNow + ":" + secondsNow;
};
function timeBasedReaction(timestampAge){
if (timestampAge >= 1800) {
clickLink('Foo');
clickLink('Bar');
} else if (timestampAge >= 300) {
clickLink('Foo');
};
};
/*
function timeBasedReaction_B(timestampAge){
if (timestampAge >= 300) {
clickLink('Foo');
if (timestampAge >= 1800) {
clickLink('Bar');
};
};
};
*/
function clickLink(linkName) {
var clickTarget = document.getElementById(linkName).children[0];
clickTarget.click();
};
function checkTimestampAge() {
console.log(reportTimestamp());
var myTimeStamp = document.getElementById('Timestamp').children[0]
var timeStampMS = new Date(myTimeStamp.textContent).getTime();
var currentDateMS = new Date().getTime();
var timestampAgeSeconds = (currentDateMS - timeStampMS)/1000;
timeBasedReaction(timestampAgeSeconds);
};
function pageInitialization() {
checkTimestampAge();
var myTimer = null;
var timerInterval = 300000;
myTimer = setInterval(function(){checkTimestampAge()},timerInterval);
}
pageInitialization();
For reasons unclear to me, running this code in the application or in a web browser starts off fine, but eventually leads to very large memory allocation.
I've tried to read
4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them,
JS setInterval/setTimeout Tutorial, and
An interesting kind of JavaScript memory leak, and it's a start, but I don't know enough to really understand what I'm doing wrong and how to correct it.
Thanks, and sorry for the huge block of text.
This causes a memory leak because of how Spotfire handles Javascript which has been associated with/loaded into a TextArea.
Both in the desktop client, as well as in the Webplayer instance, when the page is loaded, all the portions of that page are loaded, include the TextArea and including the Javascript associated therein. My previous understanding in the comments above:
"the code is intended to run when the page loads, and it was my understanding that it would stop/be cleared if the page was re-loaded or someone navigated away from it"
was incorrect. One of the script's actions was to update/redraw the HTML location in the TextArea. This, in turn, reloads the TextArea but does not clear the existing Javascript code. However, it's not really accessible anymore, either, since var myTimer = null actually creates a new myTimer rather than nulling-out the existing one. In this way, instances of myTimer increase geometrically as instances of function timeBasedReaction run and continually update the underlying TextArea and load in more of the same Javascript.
To anyone who ha a similar issue and come here, it's been over 3 months and I haven't figured out how to solve this once and for all. If I do, I'll try to come back with another update.

After opening a webpage, check if it has opened before proceeding

I am trying to create a chrome extension that, with a click of a button opens several webpages that I often visit. Currently when clicked it opens 1-4 of the 4 webpages I want it to, often stopping prematurely. The code is pretty simple, so I figured it is a processing issue. For this reason I want to introduce some delay. I've been told not to use sleep() from the research I have found so I am trying to implement code that makes my For loop wait until the page has loaded before proceeding. Here is the code:
function OpenInNewTabWinBrowser(url) {
var win = window.open(url, '_blank');
//win.focus();
}
function CheckLoading() {
return document.readyState === "interactive";
}
var websites = ['https://reddit.com', 'https://xkcd.com', 'http://poorlydrawnlines.com', 'https://explosm.net'];
var MoveAlong;
for (var i = websites.length - 1; i >= 0; i--) {
OpenInNewTabWinBrowser(websites[i]);
console.log("Just opened a window!");
MoveAlong = CheckLoading();
console.log("Just checked if it was loading!");
/*while (MoveAlong == false) {
console.log("Just realized it hasn't loaded all the way!");
sleep(10);
console.log("Just woke up!");
MoveAlong = CheckLoading();
console.log("Just double checked if it had loaded!")
}
console.log("Just broke out of the while loop!")*/
}
console.log("Just finished doing everything you asked master!")
When I run the code as is I don't always open every page. The commented section is what I have tried to utilize as a pausing function but when that code is un-commented it only opens up one page and never anymore. I have also tried supplying console.log comments for debugging but when I inspect popup if even one window opens up the console closes itself and I am left with no means of reading where the code went wrong.
I have also tried this loop and function to check for a loaded page and then unpause. This code replaced the For loop from the snippet above. It also didn't work correctly.
var i = websites.length - 1;
do {
MoveAlong = false;
OpenInNewTabWinBrowser(websites[i]);
i--;
window.onload = function() {
MoveAlong = true;
}
}
while (MoveAlong == true && i >= 0);
Any help is much appreciated. On how to properly debug, on how to detect if the website is loading, on how to make this extension work. I have been a partial lurker for a while but now I am trying to actively code every day. This is my first post and hopefully it will be the beginning to a fun hobby. Thank you again.

How do you do a page redirect after a timeout in firefox when the computer is in fast user switching (Windows 7 [winkey+L])

In Firefox with JavaScript I set up a timeout that after the timeout occurs it changes the page essentially equivalent to this:
setTimeout(function(){
window.location.href = "http://google.com";
}, 3000);
The problem is in Firefox the browser doesn't render the new contents when the computer is in fast user switching and occasionally it renders parts of the new page on top of the old one (keeping the old contents but overlaying them with parts of the new contents). To test this copy that code into the Firefox console hit winkey+L wait 3 seconds and then log back in.
I tried solutions like:
setTimeout(function(){
requestAnimationFrame(function(){
window.location.href = "http://google.com";
});
}, 3000);
Assuming that once the browser can animate it should be free to draw, but this still doesn't fix the issue (though with that change it waits for some interaction with the window after the lock to change the url in the window). I've also tried using window.location.assign and window.location.replace to no avail.
So far the only semi functional fix has been to set up event listeners for any event firing on the window and once an event fires then do the window.location.href stuff.
This appears to be a bug in Firefox, however I'm fairly certain it has been around for a while, roughly 3 years (based on bug reports in similar products within the company). What I am wondering is if there is a better method to doing this?
Interestingly if you change the window.location.href to alert() or document.write() or any function that causes a render, it fails to render properly while the computer is locked.
if your lock is from javascript you will need to yield to the main thread to allow the event queue to fire the function queued by the timeout.
for example
var items = makeLotsOfData
// bad
for(var i = 0, l = items.length; i<l; i++){
doSuperExpensiveThing(items[i]);
}
// good
function doThings(start, end, items){
for(var i = start;i<end; i++){
doSuperExpensiveThing(items[i]);
}
}
var idx = 0;
var chunk = 100;
var len = items.length;
function do(){
if(idx < len){
var end;
if(idx+chunk > len){
end = len;
}else{
end = idx+chunk
}
doThings(idx, end);
idx = end;
setTimeout(do,1);// this yeilds the next call so the event stack can run
}
}

Cross-window synchronization (critical sections) in the browser

I'm trying to achieve the following in a web page:
Users can open multiple tabs/windows of the page.
Every few seconds, I need exactly one of those tabs/windows to execute a specific section of code (critical region).
I don't care which one of the tabs/windows executes the code, i.e. no need to worry about the fairness or starvation properties of the solution.
Since the user opened the tabs/windows him/herself, the different instances of the page have no knowledge about or direct references to each other (i.e. no window.parent, etc.)
I don't want to require Flash or Silverlight or other plugins and everything needs to run client-side, so the ways in which the tabs/windows can communicate are very limited (LocalStorage is the only one I found so far, but there might be others).
Any of the tabs/windows can crash or be closed or refreshed at any time and more tabs/windows can be opened at any time also, and the remaining windows must "react" such that I still get exactly one execution of the critical region every few seconds.
This needs to run reliably in as many browsers as possible, including mobile (caniuse-rating of over %90).
My first attempt at a solution was to use a simple mutual exclusion algorithm that uses LocalStorage as the shared memory. For various reasons, I chose the mutual exclusion algorithm by Burns and Lynch from their paper "Mutual Exclusion Using Indivisible Reads and Writes" (page 4 (836)).
I built a jsfiddle (see code below) to try the idea out and it works beautifully in Firefox. If you'd like to try it, open the link to the fiddle in several (up to 20) windows of Firefox and watch exactly one of them blink orange every second. If you see more than one blink at the same time, let me know! :) (Note: the way I assign the IDs in the fiddle is a little cheesy (simply looping over 0..19) and things will only work if every window was assigned a different ID. If two windows show the same ID, simply reload one.).
Unfortunately, in Chrome and especially in Internet Explorer things don't work as planned (multiple windows blink). I think this is due to a delay in the propagation of the data I write to LocalStorage from one tab/window to the other (see my question about this here).
So, basically, I need to find either a different mutex algorithm that can handle delayed data (sounds difficult/impossible) or I need to find an entirely different approach. Maybe StorageEvents can help? Or maybe there is a different mechanism that doesn't use LocalStorage?
For completeness, here is the code of the fiddle:
// Global constants
var LOCK_TIMEOUT = 300; // Locks time out after 300ms
var INTERVAL = 1000; // Critical section should run every second
//==================================================================================
// Assign process ID
var myID;
id = window.localStorage.getItem("id");
if (id==null) id = 0;
id = Number(id);
myID = id;
id = (id+1) % 20;
window.localStorage.setItem("id", id);
document.documentElement.innerHTML = "ID: "+myID;
//==================================================================================
// Method to indicate critical section
var lastBlink = 0;
function blink() {
col = Math.round(Math.min((new Date().getTime() - lastBlink)*2/3, 255));
document.body.style.backgroundColor = "rgb(255, "+((col >> 1)+128)+", "+col+")";
}
//==================================================================================
// Helper methods to implement expiring flags
function flagUp() {
window.localStorage.setItem("F"+myID, new Date().getTime());
}
function flagDown() {
window.localStorage.setItem("F"+myID, 0);
}
// Try to refresh flag timeout and return whether we're sure that it never expired
function refreshFlag() {
content = window.localStorage.getItem("F"+myID);
if (content==null) return false;
content = Number(content);
if ((content==NaN) || (Math.abs(new Date().getTime() - content)>=timeout))
return false;
window.localStorage.setItem("F"+myID, new Date().getTime());
return Math.abs(new Date().getTime() - content) < timeout;
}
function setFlag(key) {
window.localStorage.setItem(key, new Date().getTime());
}
function checkFlag(key, timeout) {
content = window.localStorage.getItem(key);
if (content==null) return false;
content = Number(content);
if (content==NaN) return false;
return Math.abs(new Date().getTime() - content) < timeout;
}
//==================================================================================
// Burns-Lynch mutual exclusion algorithm
var atLine7 = false;
function enterCriticalRegion() {
// Refresh flag timeout and restart algorithm if flag may have expired
if (atLine7) atLine7 &= refreshFlag();
// Check if run is due
if (checkFlag("LastRun", INTERVAL)) return false;
if (!atLine7) {
// 3: F[i] down
flagDown();
// 4: for j:=1 to i-1 do if F[j] = up goto 3
for (j=0; j<myID; j++)
if (checkFlag("F"+j, LOCK_TIMEOUT)) return false;
// 5: F[i] up
flagUp();
// 6: for j:=1 to i-1 do if F[j] = up goto 3
for (j=0; j<myID; j++)
if (checkFlag("F"+j, LOCK_TIMEOUT)) return false;
atLine7 = true;
}
// 7: for j:=i+1 to N do if F[j] = up goto 7
for (j=myID+1; j<20; j++)
if (checkFlag("F"+j, LOCK_TIMEOUT)) return false;
// Check again if run is due
return !checkFlag("LastRun", INTERVAL);
}
function leaveCriticalRegion() {
// Remember time of last succesful run
setFlag("LastRun");
// Release lock on critical region
atLine7 = false;
window.localStorage.setItem("F"+myID, 0);
}
//==================================================================================
// Keep trying to enter critical region and blink on success
function run() {
if (enterCriticalRegion()) {
lastBlink = new Date().getTime();
leaveCriticalRegion();
}
}
// Go!
window.setInterval(run, 10);
window.setInterval(blink, 10);

IE stop script warning

Only in IE I get a warning when loading my site containing javascript saying that its causing the page to run slowly (and asking if I want to stop it).
I've seen other posts about this and I've looked for any long running code or infinite loops etc. The weird thing is, when I select 'No' (to not terminate the script) the page immediately loads properly. Its almost like this warning comes up right before the page is done loading. Has anybody experienced this, or know why this might be happening?
IE has its own way of making your life impossible.
Just deactivate the warning, you can further research in the future if that's necessary.
This article might help you determine why IE is giving such a warning.
Regards,
Adapted from http://www.codeproject.com/Tips/406739/Preventing-Stop-running-this-script-in-Browsers
// Magic IE<9 workaround for irritating "Stop running this script?" dialog.
RepeatOperation = function(anonymousOperation, whenToYield){
var count = 0
return function(){
if (++count >= whenToYield){
count = 0
setTimeout(function(){anonymousOperation()}, 100)
}
else anonymousOperation()
}
}
// How to implement the workaround:
//for (var i in retailers){ // Too big for IE<9! Use magic workaround:
var i = 0; var noInArray = retailers.length
var ro = new RepeatOperation(function(){
// <<Your loop body here, using return instead of continue.>>
if (++i < noInArray) ro()
else alert("Loop finished.")
}, 200) // Run this in batches of n. 500 is too much for IE<9.
ro()

Categories

Resources