For this code:
var Obj={};
for(i=0;i<=5;i++){
var add = prompt("add stuff in me!");
var last = Obj.length;
Obj.last = "add";
}
console.log(Obj);
JS Bin insists that is is potentially an infinite loop and that it will terminate subsequently.
JSBin implements infinite loop protection by injecting code into your Javascript which times the loops (see here, at about the two-minute mark). Basically, if a loop takes more than a predefined time, it will exit it with the warning and continue the code.
Your problem here is that you are waiting for user input within your loop so, unless the user can enter those six values within the timeout threshold, it will consider the loop to be infinite. You can verify this by changing the user input line into:
var add = "x"; // prompt("add stuff in me!");
Running that in JSBin shows no issues, because the user input is not delaying the loop.
The fix for this is to add // noprotect to the line to stop JSBin from erroneously considering it infinite:
for(var i=0;i<=5;i++){ // noprotect
Added note: Although the linked video says the timeout is about a second, the code seems to disagree. It states that the threshold is only a tenth of a second:
/**
* Injected code in to user's code to **try** to protect against infinite
* loops cropping up in the code, and killing the browser. Returns true
* when the loops has been running for more than 100ms.
*/
loopProtect.protect = function protect(state) {
loopProtect.counters[state.line] = loopProtect.counters[state.line] || {};
var line = loopProtect.counters[state.line];
var now = (new Date()).getTime();
if (state.reset) {
line.time = now;
line.hit = 0;
line.last = 0;
}
line.hit++;
if ((now - line.time) > 100) {//} && line.hit !== line.last+1) {
// We've spent over 100ms on this loop... smells infinite.
loopProtect.hit(state.line);
// Returning true prevents the loop running again
return true;
}
line.last++;
return false;
};
I don't know if this is what JS Bin is detecting, but there is a potential infinite loop there.
The variable i is not declared, so it is by default a global variable. The loop body calls a function, which could change the value of i (e.g. i = 0;) so that it never reaches the end, and so the loop would be infinite.
Alternatively, i could be defined as a constant (const i;), which would prevent the value from changing, again making it an infinite loop.
Related
I want to scroll down the browser window in a loop in console. I want it so that every time a scroll down (x)px down I stop, do something and then scroll (x)px again and then stop etc. until the page with ends (its a very long one, I want to parse info from it).
But when I started I stumbled upon an issue, that the scrolling function is executed only once, after the loop ends.
let i = 0;
scrollDownTillEnd = () => {
for(i; i<100; i++) {
window.scrollBy(0, 1000);
};
scrollDownTillEnd();
(it is a simplified example, but the idea should be clear)
I put the code in the console, being on a page I want to scroll, and get then the value of i at the end of the loop and only one scroll down.
Please, explain me, why this piece of code behaves like this and how to make it work, as I mentioned before (in every loop it scrolls a little bit).
Thank you!
Let me help address a few issues going on here.
1) You have an infinite loop going on because you are not checking that i is less than 100 even though you are incrementing it each time. You need to check that i < 100 so that the loop will eventually end. In your loop, 0 will always be less than 100 so the loop will never end.
2) You have a syntax error in your example because you're not actually closing out the scrollDownTillEnd function with a curly brace before calling the function itself.
3) Lastly, as good practice, you need to reset your i variable to 0 each time so that we can run this piece of code over and over again. The way you have it set up in your example, since i will be equal to 100 at the end of the first run, the loop won't ever run again after that until you reset i to 0 again. The easiest way to do this is to just initialize i to a value of 0 each time you execute this loop.
Try something like this:
scrollDownTillEnd = () => {
for(let i = 0; i < 100; i++) {
window.scrollBy(0, 1000);
};
};
scrollDownTillEnd();
You can use setInterval() since for loop will executes only once
function scrollDownTillEnd(countryDropdownList)
{
let scrollingInterval = setInterval(function(){
window.scrollBy(0,1000)
// write your condition
if (window.scrollHeight > 10000)
{
clearInterval(scrollingInterval)
}
},100)
}
scrollDownTillEnd();
Whenever I use a loop, the tab crashes. I can't seem to figure out how to work this out. I just start typing while(true) {document.getElementById("loadingText").textContent++;} and before I can type if(document.getElementById("loadingText").textContent === "100") {break;} the screen freezes and the tab crashes. Can someone help me?
Since you're using an online editor with real-time browser updates based on changes, you need to make sure you don't make your code loop infinitely. Sadly that's what while (true) { // code } does if there's no code in the statement to break the loop.
For the timebeing, until you've figured out the meat of that loop, just intentionally cause an error:
while (i <= 100) { // code }
which you can go ahead an amend until you get the desired result.
There are a couple of things you should take into account.
1) You should cache the element once so you're not repeatedly grabbing it on each loop iteration.
2) textContent is a string, so you can't use el.textContent++ as it won't be successfully evaluated.
3) You'll find it difficult to slow a traditional loop down so that you can see the number increment properly.
So here's a method using setTimeout instead of a traditional loop.
// Cache the element
const el = document.getElementById("loadingText");
(function displayNumber(n, end) {
// Repeat until the iteration number (n)
// is 10 (for this example)
if (n <= end) {
el.textContent = n;
// Wait 0.5s then call the function again with an increased n
setTimeout(() => displayNumber(++n, end), 500);
}
// Pass in the initial number, and the end limit
}(1, 10));
<div id="loadingText"></div>
You are asking if your property is === "100" as ++ increment the numeric value, the loop never breaks, compare to 100 instead of "100" as string
if(document.getElementById("loadingText").textContent == "100") {break;}
or
if(document.getElementById("loadingText").textContent === 100) {break;}
I dont think that a textContent could be incremented. Try Node.value++.
If this still doesn't works. To run an infinite loop you can use setInterval(); method.
example:
var i=0;
var z = setInterval(foo,10);
function foo() { alert(i); }
In the above example the function foo() will execute infintely with a gap of 10 mili seconds. You can put it to 0 ms as well if you wish to.
I am trying to get the position of a moving div(animated using css3 animations) and for checking it continuously I am using a while(true) like below
function detectCollision(){
alert(document.getElementById("obstacle").style.left)
while(true){
var temp = getObstaclePosition();
var temp2 = getPlanePosition();
if(temp[0] <= temp2[0]+500){
document.getElementsByClassName("plane")[0].style.display = "none";
break;
}
}
}
The problem here is that after the first alert the page hangs. Moreover if I put an alert in the while loop then it keeps on popping up and the code works fine but not otherwise.
Let me know how I can fix this?
Instead of using a while (true), which is costly, you can use setInterval:
a = setInterval(function () {
var temp = getObstaclePosition();
var temp2 = getPlanePosition();
if(temp[0] <= temp2[0]+500){
document.getElementsByClassName("plane")[0].style.display = "none";
clearInterval(a);
}
}, 100);
The page does not render while you are inside that loop, and thus the position of the element will not change. This results in the loop never ending.
If you want to do something like this, you will have to be using a recursive setTimeout or a normal setInterval implementation.
With them, do one check per timeout.
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
JavaScript runs in one and the same thread, so if some loop occupies this with an infinite loop, the rest is not able to run anymore.
As an alternative to your loop, you could use setInterval, which repeatedly executes a function or code snippet:
setInterval(new function() {
// whatever
}, 100); // repeat every 100 ms, i.e. 10 times per second
note: I may be using the word recursively wrong
I'm trying to create a script that runs through a long string of text, searching for the recurrence of substrings. For this I essentially do a for-loop from 12 to 3 (lengths of strings I want to check for recurrence), and for each of those lengths I use another for loop to define if the string of that length is present multiple times. A dumbed down example (untested, just for demonstration):
for(var len=12; len>3; len--) {
var recurring = [];
for(var pos = 0; pos < (inputString.length - len); pos++) {
var mystring = inputString.substr(pos, len);
// do a regex to check if mystring occurs more than once
// if so, push it into recurring and remove from inputString to prevent partial duplicates when mystring gets shorter
}
// output recurring strings
}
This all works, but obviously when I run this on a very long inputString it gets very slow. In fact, it freezes the browser, and any progress output that I want to show while the script runs is paused and shows up all at once when the script is done.
To prevent this, I created a function that is supposed to run through the for-loop in small batches at a time, using a callback function to proceed to the next batch. By using a basic version of this, I was able to output progress onto the page during what used to be a for-loop, even if it's going through thousands of loops:
var fragmentedFor = function($aFragmentLength, $aTotal, $aFunction, $aCallback)
{
var $lStart = 0,
$lFragmentLength = $aFragmentLength,
$lTotal = $aTotal,
$lFunction = $aFunction,
$lCallback = $aCallback;
// simulate a for loop, but in fragments to prevent the browser from freezing
(function forLoop ($aStart)
{
// run x loops at a time
var $lEnd = $lStart + $lFragmentLength;
// can't run the loop past the total length
if($lEnd > $lTotal )
$lEnd = $lTotal;
// the fragmented for loop
for(var $i = $lStart; $i < $lEnd; $i++) {
// run the function here
$lFunction({count: $i});
}
// next time, start from where we just left
$lStart += $lFragmentLength;
// is there room for more loops?
if($lStart < $aTotal) {
setTimeout(forLoop, 10);
} else {
// if not, run the callback
$lCallback();
}
})();
}
When I run this just once it seems to do what I want:
function myLoop(x) {
new fragmentedFor(
10, // amount of loops per run
x, // total amount of loops
function($aData) { console.log($aData.count); },
function() { console.log('finished') }
);
}
myLoop(1000);
However, when I want to call this one nested within another instance of such a loop, I run into trouble:
new fragmentedFor(
1,
5,
function($aData) { myLoop($aData.count * 100) },
function() { console.log('finished all') }
)
(I have copied my current files here: http://files.litso.com/vigenere/so/, which includes the fragmentedFor function so you can essentially paste these two functions in the console and see the result. This was a lot easier than putting it on jsfiddle and making that work, if you guys don't mind)
What appears to be happening is that the first run of myLoop is started, but since it's trying to run 0 * 100 times it ends safely, going to the second run. The second run of myLoop should go up to 100, but it runs to 19 before suddenly run 3 of myLoop kicks in.
From there on, the output seems to be totally mixed up, I figure because my callbacks are implemented incorrectly and the loops are not really waiting for eachother to finish.
What am I doing wrong here? How can I even start debugging where the problem lies, and how can I make sure that the independent runs of the for loop actually wait for eachother to finish?
-edit-
Here's a jsfiddle of an older working copy: http://jsfiddle.net/u2aKX/
This did not incorporate the fragmentedFor loop, it does have some callback functions which seemed to improve the performance compared to regular nested for-loops by 100%.
I figure because my callbacks are implemented incorrectly and the loops are not really waiting for eachother to finish.
What am I doing wrong here?
Your fragmentedFor expects the $aFunction to be synchronous, and as soon as it returns it schedules the next loop turn.
Yet, by using a nested fragmentedFor in that function, you're making it asynchronous - and the iterations of the inner loops will start to crossfire.
As you recogniced, you will have to let them wait to finish - which means hooking the next iteration on the callback of the previous one. You will have to write another fragmentedFor that deals with asynchronous iteration steps:
function fragmentedForAsync(from, to, body, callback) {
var i = from;
(function loop() {
if (i < to)
body(i++, loop); // pass itself as callback
else
callback();
})();
}
and then use it like this:
fragmentedForAsync(1, 5, function(i, callback) {
fragmentedFor(10, i*100, function(j) {
console.log(j);
}, function() {
console.log('finished '+1);
callback(); // next turn of outer loop
});
}, function() {
console.log('finished all')
});
I have a JavaScript function that contains a for loop that iterates so many times.
After calling this function, the IE browser displays this message:
Stop running this script?
A script on this page is causing your web browser to run slowly.
If it continues to run, your computer might become unresponsive.
How can I fix this?
is there anyway I can disable this message from IE?
This message displays when Internet Explorer reaches the maximum number of synchronous instructions for a piece of JavaScript. The default maximum is 5,000,000 instructions, you can increase this number on a single machine by editing the registry.
Internet Explorer now tracks the total number of executed script statements and resets the value each time that a new script execution is started, such as from a timeout or from an event handler, for the current page with the script engine. Internet Explorer displays a "long-running script" dialog box when that value is over a threshold amount.
The only way to solve the problem for all users that might be viewing your page is to break up the number of iterations your loop performs using timers, or refactor your code so that it doesn't need to process as many instructions.
Breaking up a loop with timers is relatively straightforward:
var i=0;
(function () {
for (; i < 6000000; i++) {
/*
Normal processing here
*/
// Every 100,000 iterations, take a break
if ( i > 0 && i % 100000 == 0) {
// Manually increment `i` because we break
i++;
// Set a timer for the next iteration
window.setTimeout(arguments.callee);
break;
}
}
})();
The unresponsive script dialog box shows when some javascript thread takes too long too complete. Editing the registry could work, but you would have to do it on all client machines. You could use a "recursive closure" as follows to alleviate the problem. It's just a coding structure in which allows you to take a long running for loop and change it into something that does some work, and keeps track where it left off, yielding to the browser, then continuing where it left off until we are done.
Figure 1, Add this Utility Class RepeatingOperation to your javascript file. You will not need to change this code:
RepeatingOperation = function(op, yieldEveryIteration) {
//keeps count of how many times we have run heavytask()
//before we need to temporally check back with the browser.
var count = 0;
this.step = function() {
//Each time we run heavytask(), increment the count. When count
//is bigger than the yieldEveryIteration limit, pass control back
//to browser and instruct the browser to immediately call op() so
//we can pick up where we left off. Repeat until we are done.
if (++count >= yieldEveryIteration) {
count = 0;
//pass control back to the browser, and in 1 millisecond,
//have the browser call the op() function.
setTimeout(function() { op(); }, 1, [])
//The following return statement halts this thread, it gives
//the browser a sigh of relief, your long-running javascript
//loop has ended (even though technically we havn't yet).
//The browser decides there is no need to alarm the user of
//an unresponsive javascript process.
return;
}
op();
};
};
Figure 2, The following code represents your code that is causing the 'stop running this script' dialog because it takes so long to complete:
process10000HeavyTasks = function() {
var len = 10000;
for (var i = len - 1; i >= 0; i--) {
heavytask(); //heavytask() can be run about 20 times before
//an 'unresponsive script' dialog appears.
//If heavytask() is run more than 20 times in one
//javascript thread, the browser informs the user that
//an unresponsive script needs to be dealt with.
//This is where we need to terminate this long running
//thread, instruct the browser not to panic on an unresponsive
//script, and tell it to call us right back to pick up
//where we left off.
}
}
Figure 3. The following code is the fix for the problematic code in Figure 2. Notice the for loop is replaced with a recursive closure which passes control back to the browser every 10 iterations of heavytask()
process10000HeavyTasks = function() {
var global_i = 10000; //initialize your 'for loop stepper' (i) here.
var repeater = new this.RepeatingOperation(function() {
heavytask();
if (--global_i >= 0){ //Your for loop conditional goes here.
repeater.step(); //while we still have items to process,
//run the next iteration of the loop.
}
else {
alert("we are done"); //when this line runs, the for loop is complete.
}
}, 10); //10 means process 10 heavytask(), then
//yield back to the browser, and have the
//browser call us right back.
repeater.step(); //this command kicks off the recursive closure.
};
Adapted from this source:
http://www.picnet.com.au/blogs/Guido/post/2010/03/04/How-to-prevent-Stop-running-this-script-message-in-browsers
In my case, while playing video, I needed to call a function everytime currentTime of video updates. So I used timeupdate event of video and I came to know that it was fired at least 4 times a second (depends on the browser you use, see this). So I changed it to call a function every second like this:
var currentIntTime = 0;
var someFunction = function() {
currentIntTime++;
// Do something here
}
vidEl.on('timeupdate', function(){
if(parseInt(vidEl.currentTime) > currentIntTime) {
someFunction();
}
});
This reduces calls to someFunc by at least 1/3 and it may help your browser to behave normally. It did for me !!!
I can't comment on the previous answers since I haven't tried them. However I know the following strategy works for me. It is a bit less elegant but gets the job done. It also doesn't require breaking code into chunks like some other approaches seem to do. In my case, that was not an option, because my code had recursive calls to the logic that was being looped; i.e., there was no practical way to just hop out of the loop, then be able to resume in some way by using global vars to preserve current state since those globals could be changed by references to them in a subsequent recursed call. So I needed a straight-forward way that would not offer a chance for the code to compromise the data state integrity.
Assuming the "stop script?" dialog is coming up during a for() loop executuion after a number of iterations (in my case, about 8-10), and messing with the registry is no option, here was the fix (for me, anyway):
var anarray = [];
var array_member = null;
var counter = 0; // Could also be initialized to the max desired value you want, if
// planning on counting downward.
function func_a()
{
// some code
// optionally, set 'counter' to some desired value.
...
anarray = { populate array with objects to be processed that would have been
processed by a for() }
// 'anarry' is going to be reduced in size iteratively. Therefore, if you need
// to maintain an orig. copy of it, create one, something like 'anarraycopy'.
// If you need only a shallow copy, use 'anarraycopy = anarray.slice(0);'
// A deep copy, depending on what kind of objects you have in the array, may be
// necessary. The strategy for a deep copy will vary and is not discussed here.
// If you need merely to record the array's orig. size, set a local or
// global var equal to 'anarray.length;', depending on your needs.
// - or -
// plan to use 'counter' as if it was 'i' in a for(), as in
// for(i=0; i < x; i++ {...}
...
// Using 50 for example only. Could be 100, etc. Good practice is to pick something
// other than 0 due to Javascript engine processing; a 0 value is all but useless
// since it takes time for Javascript to do anything. 50 seems to be good value to
// use. It could be though that what value to use does depend on how much time it
// takes the code in func_c() to execute, so some profiling and knowing what the
// most likely deployed user base is going to be using might help. At the same
// time, this may make no difference. Not entirely sure myself. Also,
// using "'func_b()'" instead of just "func_b()" is critical. I've found that the
// callback will not occur unless you have the function in single-quotes.
setTimeout('func_b()', 50);
// No more code after this. function func_a() is now done. It's important not to
// put any more code in after this point since setTimeout() does not act like
// Thread.sleep() in Java. Processing just continues, and that is the problem
// you're trying to get around.
} // func_a()
function func_b()
{
if( anarray.length == 0 )
{
// possibly do something here, relevant to your purposes
return;
}
// -or-
if( counter == x ) // 'x' is some value you want to go to. It'll likely either
// be 0 (when counting down) or the max desired value you
// have for x if counting upward.
{
// possibly do something here, relevant to your purposes
return;
}
array_member = anarray[0];
anarray.splice(0,1); // Reduces 'anarray' by one member, the one at anarray[0].
// The one that was at anarray[1] is now at
// anarray[0] so will be used at the next iteration of func_b().
func_c();
setTimeout('func_b()', 50);
} // func_b()
function func_c()
{
counter++; // If not using 'anarray'. Possibly you would use
// 'counter--' if you set 'counter' to the highest value
// desired and are working your way backwards.
// Here is where you have the code that would have been executed
// in the for() loop. Breaking out of it or doing a 'continue'
// equivalent can be done with using 'return;' or canceling
// processing entirely can be done by setting a global var
// to indicate the process is cancelled, then doing a 'return;', as in
// 'bCancelOut = true; return;'. Then in func_b() you would be evaluating
// bCancelOut at the top to see if it was true. If so, you'd just exit from
// func_b() with a 'return;'
} // func_c()