Stop Javascript function if it's freeze to long - javascript

I've got
function doSome(){}
What should I do if I want stop execution of this function when it's executing more then 200 mills?
Thanks a lot.

I guess 200 milliseconds it's a little to short, but:
if you have a while loop, create a Date object before entering the loop and a new one in the loop. If the difference is greater than 200, break it
start = new Date()
while (1) {
end = new Date()
if (end - start > 200) break
// work
{
If you're executing Ajax call, see aborting AJAX call

A web worker can be terminated with its terminate function. There is no other way to interrupt execution of Javascript from within Javascript itself. Note that many browsers also have slow script detection which prompts the user when a script runs a certain amount of time or number of instructions. This is not controllable from Javascript, however.

Depending on the kind of algorithm that is implemented within the function, you could check how long it's been running, but it isin't very precise and you would have to make sure that each loop iteration takes very little time to process.
For instance:
function doSomething() {
var startTime = new Date.now(),
i = -1,
itemsToProcess = [1, 2, 3, 4],
len = itemsToProcess.length;
while ((Date.now() - startTime) <= 200 && ++i < len) {
//process items here
}
}
You could also avoid the freezing by giving the browser some time to breath using
setTimeout or setInterval. That is probably a better approach if you want to task to always complete.
function startDoSomething(items, delay) {
var i = arguments[2] || 0;
//process
console.log('processing item at: ' + i);
if (++i < items.length) {
setTimeout(function () {
startDoSomething(items, delay, i);
}, delay);
}
}
Finally, a much more efficient way to handle long-running processes would be to use a web worker, you can't directly update the DOM from the worker.

Related

calling function in a loop in javascript

I am trying to call a function from within a loop in a way that when the function finishes executing, the loop continues to the next iteration and calls it again. But instead the loop doesn't wait for the function to finish and instead calls 4 instances of the function and runs them at the same time! Should I put the whole function in the loop or is there to make the loop wait for the function to be executed? Thanks
for (var i=2; i<=4; i++){
galleryAnimation(i); //This is executed 3 times at once
}
function galleryAnimation(i){
$("#pic" + i).delay(delayTime).fadeIn(duration);
}
The function is being executed 3 times just like you requested, the problem is that both delay and fadeIn use timers: they set a timer in the future when the function will be executed and return immediately: they are non-blocking calls. So, in your case, because you're calling the function 3 times at, let's say, 0.0001s, 0.0002s, and 0.0003s, the three kick in at, let's say, 5.0001, 5.0002 and 5.0003.
What you had in mind were blocking calls to these functions. You expected the whole execution to stop until the animations were finished. This would stop the whole browser Javascript engine for the duration, meaning no other animation or user javascript interaction would be possible.
To solve it, you have to use callbacks. You can supply a function to fadeIn that will be called once the animation has completed:
http://api.jquery.com/fadeIn/
You can use queues to simulate the same on delay():
Callback to .delay()
Simplistic solution: Increase the timeout by a factor every time.
var i, factor,
duration = 250,
delayTime = 500;
for (i = 2, factor = 0; i <= 4; i++, factor++) {
galleryAnimation(i, factor);
}
function galleryAnimation(i, factor) {
$("#pic" + i).delay(factor * delayTime).fadeIn(duration);
}
This runs the same way your approach does, only the delays get longer every time.
Generic solution 1 - work with setInterval() to have your worker function (the one that does the fadeIn) called in predefined intervals:
var elements = $("#pic2,#pic3,#pic4").toArray(), // or any other way to select your elements
duration = 250,
delayTime = 500,
intervalId = setInterval(function () {
$(elements.shift()).fadeIn(duration);
if (elements.length === 0) {
clearInterval(intervalId);
}
}, delayTime);
Generic solution 2 - work with callbacks that are called when the previous animation finishes:
var elements = $("#pic2,#pic3,#pic4").toArray(), // or any other way to select your elements
duration = 250,
delayTime = 500,
next = function () {
$(elements.shift()).delay(delayTime).fadeIn(duration, next);
};
next(); // start the chain
one thing you can do, is to use an identifier (boolean) and then, in the loop, you test the identifier to decide if the loop can continue or stop.
For example,
function galleryAnimation(i, iBool){
$("#pic" + i).delay(delayTime).fadeIn(duration);
iBool = 0;
}
Then on the return;
for (var i=2; i<=4; i++){
galleryAnimation(i, iBool);
// If iBool = 0 then continue
// Else Break
}
that might be a solution, as the loop will need the returning value to determine the next step, to continue or break.
I came with my own solution that seemed to work perfectly, this is my code:
function fadeInAnimation(index){
$("#pic" + index).delay(delayTime).fadeIn(duration, function(){
photoIndex();
});
}
function photoIndex(){
index++;
fadeInAnimation(index);
}
fadeInAnimation(index);
});
I have realized that using loops is a really bad idea with things like this. This code will allow me to add as many images as I want just by renaming them in the folder rather than coding them in manually as callbacks for each photo.
Thanks for those who answered. Your suggestions are great and will definitely remember them in other applications like this one

Function to slow down a loop using callbacks gets messed up when used recursively

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')
});

JS hangs on a do while loop

I am wondering how I can solve a hanging page with JS.
I have a JS loop which I'm testing like this, but it seems to hang forever - is there a way to stop it hanging while still completing the script ?:
<div id="my_data"></div>
<script>
function test(value){
output= [];
do{
value++;
output.push(value);
document.getElementById('my_data').innerHTML = (output.join(''));
}while(value < 10000000);
alert('end'); // never occurs
}
test(0);
</script>
You're updating the DOM with an increasingly long string ten million times.
If you're some sort of super-being from the future, then maybe this makes sense.
Javascript is single-threaded, so nothing else can happen while it's running your loop. It's running ten million times, and on each run it's setting the innerHTML of #my_data to something new. It's very inefficient and seems useless. What are you trying to accomplish?
You are concatenating 10,000,000 consecutive numbers together into a string, which really will not work well on any modern supercomputer.
If you'd like to poll the progress, setup a timer to do it somewhat slower. It isn't practical, but it looks nice: http://jsfiddle.net/V6jjT/2/
HTML:
<div id="my_data"></div>
Script:
var value = 0;
var interval = setInterval(function() {
if (value > 100) {
clearInterval(interval);
return;
}
value++;
document.getElementById('my_data').innerHTML += ' ' + value;
}, 10);
Running a tight loop like that will not update anything until it's done; besides, 10M array items?!
If you really want to do this and not let the browser hang until forever you have to use setInterval to allow the browser to refresh in between.
function updater(value, max, interval) {
var output = [],
node = document.getElementById('my_data'),
tid = setInterval(function() {
if (value++ < max) {
output.push(value);
node.innerHTML = (output.join(''));
} else {
alert('done');
clearInterval(tid);
}
}, interval);
}
updater(0, 10, 200);
You should not update the HTML each iteration.
function test(value){
output= [];
do {
value++;
output.push(value);
} while(value < 10000000);
document.getElementById('my_data').innerHTML = (output.join(''));
alert('end'); // never occurs
}
should work (although 10 million numbers in a HTML will still need their time to render).
If you want to see numbers running, you should have a look at window.setInterval - the browser needs some time between js execution to refresh its view. So, you will have to rewrite your code running several chunks of data asynchrously. See also:
Running a long operation in javascript? (example code for your loop)
How can I force the browser to redraw while my script is doing some heavy processing?
Javascript async loop processing
Prevent long running javascript from locking up browser
DOM refresh on long running function
Best way to iterate over an array without blocking the UI
Javascript - how to avoid blocking the browser while doing heavy work?
Tight loop is likely to always exhaust computing power. I would do it in chunks. Say, divide your list into smaller lists and pause 5ms in between lists. I can provide an example if you need.

for loop delay in javascript

How to set delay in for loop execution
for(i=0; i<=10;i++){
var s=i;//This line should execute for every 2 secs only
}
How to give loop delay in java script....
I dont want like below..I want without using setTimeout...
for(i=0; i<=10;i++){
setTimeout("setvalue()",2000); //This alert should display for every 2 secs only
}
function setvalue()
{
var s=i;
}
please help me...
Use setInterval()
var i = 0;
var interval = setInterval(function(){
setValue();
i += 1;
if(i == 10)
clearInterval(interval);
}, 2000);
There is no way to sleep for 2sec without freezing the whole browser. Javascript is single threaded.
You can't. JS runs in a single thread and any attempt to delay that thread will freeze the entire page. Using setTimeout is your only option.
EDIT: or setInterval; either way, there is no non-hairy way to express "halt execution here for x milliseconds."
Using setTimeout is inevitable, however, a recursive function might be a better solution for this one:
var i=0;
function recurs() {
i = s;
i++;
if (i <= 10) recurs();
}
recurs();
As others have stated, setTimeout can be used very well to handle these sorts of scenarios and setInterval could also be used but is discouraged by some.
You can even recursively call a function that has setTimeout built into it as mentioned in the MDN documentation of setInterval. The heading there mentions 'dangerous usage' but their solution to the danger is the block of code beneath.
There it mentions that to have a loop executing every x seconds (or milliseconds) then you can do the following and know for sure that the functions will only be executing one at a time and in-sequence:
(function loop(){
setTimeout(function(){
// logic here
// recurse
loop();
}, 1000); // repeat loop 1 second after this branch has completed
})();
And if you want it to only do that a limited number of times, then you can create a variable outside of the loop and only recursively execute if the count is smaller than the number of times you want to execute for. Such as this:
var count = 0;
(function loop() {
setTimeout(function() {
// logic
count++;
if (count < 10) {
loop();
}
}, 1000);
})();

Executing 'realtime' javascript without hanging the browser

I'm trying to write a simple music-sequencer in Javascript.
Sounds will be played by SoundManager2
I quickly realised that setTimeout and setInterval are useless for this kind of timing. Their accuracy is simply not good enough.
So what I am trying instead is this:
Create a queue of sound-events (a 2d array [ note, time ] )
Process the queue in a while loop
In pseudo code it could look like this:
// The queue of time/note values (millisecs)
var q = [[0, C], [250, D], [500, E]]
var begin = (new Date).getTime()
while(q.length > 0 ){
var now = (new Date).getTime()
var eventTime = q[0][0] + begin
if( now >= eventTime){
playNote(q[0][1]) // Play the note
q.shift() // Now that the note has been played, discard it.
}
}
Through debugging I've found that this method seems precise enough (the playNote call is made at the exact time it's supposed to).
However while the 'sequence' is playing, all other Javascript (including the bit that actually MAKES the sound) is suspended which is hardly a surprise.
This means that I have silence for as long time it takes to run the sequence and then ALL of the calls to playNote are executed.
I've tried isolating the while-loop in it's own function and then call it through setTimeout, in the hopes that this would create a background thread executing it's own thing (namely playing the sequence) without halting all other JS execution. That doesn't work
I've also tried calling the playNote function using setTimeout, in the hopes that while indeed the UI is frozen at least the sequence is playing correctly, but that doesn't work either.
I've also tried a combination, but as you probably already guessed, that doesn't work either.
So my question is:
How can I have a queue of events processed with precise timing, while NOT shutting down all other code-execution during the run
I don't know if there is a solution for older browers, but web workers are intented for doing parrallel execution in JavaScript.
They are supported in recent versions of Chrome and Firefox, I don't know about other browsers.
If you replace your while loop with a recursive function (it doesn't have to be self-executing) the browser wont freeze.
(function foo (q) {
var now = (new Date).getTime(),
eventTime = q[0][0] + begin;
if (q.length > 0) {
if (now >= eventTime) {
playNote(q[0][1]); // Play the note
q.shift(); // Discard played note
}
return foo(q); // Pass q into the next round
} else {
return; // End the "loop"
}
}(q));
Edit: The problem with recursive calls + timer accuracy should be taken care of by setInterval:
var timer = setInterval(function foo () {
var now = (new Date).getTime(),
eventTime = q[0][0] + begin;
if (q.length > 0) {
if (now >= eventTime) {
playNote(q[0][1]); // Play the note
q.shift(); // Discard played note
}
} else {
clearInterval(timer); // End the "loop"
}
}, 10); // This is in ms (10 is just a random number)

Categories