It seems that everyone has a few problems with clearInterval. I have built a slider that allows people to hover a click on arrows. The banner also rotates ever few seconds. I want to be able to have the auto-rotate turn off after someone clicks on one of the arrows.
Here's my code:
$(function(){
var intvl = 0;
intvl = setInterval(heroTransitionNext, 2000);
$('.rightArrow').click(function(){
window.clearInterval(intvl);
});
});
EDIT:
Here is the function it is calling:
function heroTransitionNext() {
$('.HP-hero li').filter(':visible').fadeOut('normal', function () {
if ($(this).next().length != 0) {
activeZone = parseInt(activeZone) + 1;
$(this).next().fadeIn('normal', heroNavHighlight(activeZone));
} else {
activeZone = 1;
$('.HP-hero li:first-child').fadeIn('normal', heroNavHighlight(activeZone));
}
$(this).hide();
});
};
To stop the animation you can use jquery's .stop() but not sure whether it'll solve the problem or not that you are facing (didn't visualize) but you can give it a try
$('.HP-hero li').stop(1,1); // or try $('.HP-hero li').stop()
window.clearInterval(intvl);
As say2joe said that clearInterval will just stop the function from invoking next time but it won't clear the current queue (he is right) so in that case stop could be used.
About Stop.
Depending on how much work your heroTransitionNext function is doing, it may still be executing even though the interval is cleared -- in other words, clearing the interval will stop the function from being invoked -- but, any instance of the function(s) executing in memory will continue to execute until finished.
To be more clear, here's a use case (you can check this out yourself by using a profiler in Firebug or Developer Tools):
heroTransitionNext execution time is 2.1 seconds.
clearInterval is invoked 6.1 seconds after setInterval is invoked.
At 6.1 seconds, heroTransitionNext has been invoked four times. The first three executions have completed, however, the fourth will not complete until it finishes executing (at 8.1 seconds since setInterval was called). Note: In this use case, each successive invokation will execute while the last invokation's execution is still continuing (for 100 more ms) -- in other words, you'll have execution overlap from 2 to 2.1, 4 to 4.1, and 6 to 6.1 second intervals.
If the function takes longer to execute than the interval set, use a recursive function with setTimeout(). The following link will give you a good example.
Also, a good reference for explanation is https://developer.mozilla.org/en/DOM/window.setInterval.
Related
Say I have 20 rows of JS code and I want the interpreter to execute only half of the code (<11 rows), then stop, without functions and returns, or without commenting the rest of the code (I already tried a return, see in advance).
A location.reload(true); in row 10 is a close solution but I'm looking for a client side stop.
My question
Is there like a stop command (or functionality) in JavaScript, that asks the interpreter to stop and behave as if no code ran so far?
Why I ask
The background for this question is a problem I have calling a function in more than one keydown event.
Given the keydown event is triggered only once, I consider sending the interpreter back to the start after the keydown event was triggered disposably, and without refreshing the page (Sorry if it seems absurd, I'm new to JS and failed finding the source of the bug).
Of course, the above question is different than the question "why does the keydown event triggered only once", which I already asked here - here's a link for context.
Preventing an XY problem
On one hand, I want to make sure there is no XY problem. On the other hand, I am not allowed to copywrite the previous question to this session hence linked to it above.
Either way, I would be glad to know if what I just described (client side stop of a JS interpreter) is even possible in the current release of the language.
Note: I decided to carefully rewrite the question after some comments earlier today (there were no answers) and did my best ensuring the question is informative and communal.
There is no stop command, but I experienced the need of it before when there was a long-running client-side operation.
The solution:
1) Divide the problem into small packets
2) Make sure you are able to make your function work only for activeMilliseconds milliseconds:
function doStuff(packets, currentIndex, activeMilliseconds) {
var start = new Date(); //Start of chunk
while((currentIndex < packets.length) && (new Date() - start < activeMilliseconds)) {
//Do something with packets[currentIndex]
currentIndex++;
}
return currentIndex;
}
3) Now that we are able to work for activeMilliseconds milliseconds, we need to use this asynchronously:
//Define packets
var currentIndex = 0;
var intervalID = setTimeout(function() {
If(currentIndex = doStuff(packets, currentIndex, activeMilliseconds) >= packets.length) clearInterval(intervalID);
}, totalMilliseconds);
Node: totalMilliseconds > activeMilliseconds should be true. For example, if totalMilliseconds is 250, and activeMilliseconds is 200, then in each 250 milliseconds a chunk will run for 200 milliseconds, leaving the browser to do its stuff for 50 milliseconds every 250 milliseconds even if there is a lot of work to do.
4) Make sure a job stops a previous similar job:
function doJob(packets, intervalID, activeMilliseconds, totalMilliseconds) {
clearInterval(intervalID);
//Define packets
var currentIndex = 0;
var intervalID = setTimeout(function() {
If(currentIndex = doStuff(packets, currentIndex, activeMilliseconds) >= packets.length) clearInterval(intervalID);
return intervalID;
}, totalMilliseconds);
}
If you use this idea for your key event, then it will stop the previous keyboard, your maximum wait time to do so will be activeMilliseconds, which is an acceptable compromise in my opinion.
That said, this methodology should be only used in the case when you have no other option. You need to know that Javascript has a single thread, so even if you trigger a function execution while a previous instance of the event is still running, your new event will sequentially be executed when the other event is finished.
I'm writing a "Game of Life" in javascript. I have all the logic done in a function called doGeneration(). I can repeatedly call this from the console and everything goes as planned, however, if I put it in a while loop the execution blocks the UI and I just see the end result (eventually).
while (existence) {
doGeneration();
}
If I add a setTimeout(), even with a generation limit of say 15, the browser actually crashes (Canary, Chrome).
while (existence) {
setTimeout(function() {
doGeneration();
},100);
}
How can I call doGeneration() once every second or so without blocking the DOM/UI?
You want setInterval
var intervalId = setInterval(function() {
doGeneration();
}, 1000);
// call this to stop it
clearInterval(intervalId);
I would use requestAnimationFrame(doGeneration). The idea of this function is to let the browser decide at what interval the game logic or animation is executed. This comes with potential benefits.
https://hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
Rather than using setINterval or setTimeout and assume some random time interval will be enough for the UI to update you shoul/could make the doGeneration smart enough to call itself after dom was updated and if the condition of existence is satisfied.
So I've got a scroll event. It does a load of stuff to work out whether something should be moved on the page. When you scroll down, it fires off. If you wheel down, drag, it fires of bazillions and bazillions of times. As you'd expect, perhaps. Here's some simple dummy code to represent the sequence of events.
function scroller() {
// 1. A really expensive calculation that depends on the scroll position
// 2. Another expensive calculation to work out where should be now
// 3. Stop current animations
// 4. Animate an object to new position based on 1 and 2
}
$(window).on('resize' scroller);
Don't get me wrong, it's usually accurate so there isn't so much a concurrency issue. My animations inside the event call .stop() (as part #3) so the latest version is always* the right one but it's eating up a lot of CPU. I'd like to be a responsible developer here, not expecting every user to have a quad core i7.
So to my question... Can I kill off previous calls to my method from a particular event handler? Is there any way I can interfere with this stack of queued/parallel-running "processes" so that when a new one is added to the stack, the old ones are terminated instantly? I'm sure there's a concurrency-minded way of wording this but I can't think of it.
*At least I think that's the case - if the calculations took longer in an earlier run, their animation could be the last one to be called and could cock up the entire run! Hmm. I hadn't thought about that before thinking about it here. Another reason to stop the previous iterations immediately!
You can ensure the event is fired a maximum of once per x milliseconds. E.g.:
(function ($) {
$.fn.delayEvent = function (event, callback, ms) {
var whichjQuery = parseFloat($().jquery, 10)
, bindMethod = whichjQuery > 1.7 ? "on" : "bind"
, timer = 0;
$(this)[bindMethod](event, function (event) {
clearTimeout (timer);
timer = setTimeout($.proxy(callback, this, event), ms);
});
return $(this);
};
})(jQuery);
$(window).delayEvent("resize", scroller, 1000);
Minimalistic demo: http://jsfiddle.net/karim79/z2Qhz/6/
Context
I've got about 10 complex graphs which take 5sec each to refresh. If I do a loop on these 10 graphs, it takes about 50 seconds to refresh. During these 50 seconds, the user can move a scrollbar. If the scrollbar is moved, the refresh must stop and when the scrollbar stops to move, the refresh occurs again.
I'm using the setTimeout function inside the loop to let the interface refresh.
the algorithm is :
render the first graph
setTimeout(render the second graph, 200)
when the second graph is rendered, render the third one in 200ms, and so on
The setTimeout allows us to catch the scrollbar event and to clearTimeout the next refresh to avoid to wait 50sec before moving the scrollbar...
The problem is that it does not run anytime.
Take the simple following code (you can try it in this fiddle : http://jsfiddle.net/BwNca/5/) :
HTML :
<div id="test" style="width: 300px;height:300px; background-color: red;">
</div>
<input type="text" id="value" />
<input type="text" id="value2" />
Javascript :
var i = 0;
var j = 0;
var timeout;
var clicked = false;
// simulate the scrollbar update : each time mouse move is equivalent to a scrollbar move
document.getElementById("test").onmousemove = function() {
// ignore first move (because onclick send a mousemove event)
if (clicked) {
clicked = false;
return;
}
document.getElementById("value").value = i++;
clearTimeout(timeout);
}
// a click simulates the drawing of the graphs
document.getElementById("test").onclick = function() {
// ignore multiple click
if (clicked) return;
complexAlgorithm(1000);
clicked = true;
}
// simulate a complexe algorithm which takes some time to execute (the graph drawing)
function complexAlgorithm(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
document.getElementById("value2").value = j++;
// launch the next graph drawing
timeout = setTimeout(function() {complexAlgorithm(1000);}, 1);
}
The code does :
when you move your mouse into the red div, it updates a counter
when you click on the red div, it simulates a big processing of 1sec (so it freezes the interface due to javascript mono thread)
after the freezing, wait 1ms, and resimulate the processing and so on until the mouse move again
when the mouse move again, it breaks the timeout to avoid infinite loop.
The problem
When you click one time and move the mouse during the freeze, I was thinking that the next code that will be executed when a setTimeout will occurs is the code of the mousemove event (and so it will cancel the timeout and the freeze) BUT sometimes the counter of click gains 2 or more points instead of gaining only 1 point due to the mouvemove event...
Conclusion of this test : the setTimeout function does not always release resource to execute a code during a mousemove event but sometimes kept the thread and execute the code inside the settimeout callback before executing another code.
The impact of this is that in our real example, the user can wait 10 sec (2 graphs are rendered) instead of waiting 5 seconds before using the scrollbar. This is very annoying and we need to avoid this and to be sure that only one graph is rendered (and other canceled) when the scrollbar is moved during a render phase.
How to be sure to break the timeout when the mouse move ?
PS: in the simple example below, if you update the timeout with 200ms, all runs perfectly but it is not an acceptable solution (the real problem is more complex and the problem occurs with a 200ms timer and a complex interface). Please do not provide a solution as "optimize the render of the graphs", this is not the problem here.
EDIT : cernunnos has a better explanation of the problem :
Also, by "blocking" the process on your loop you are ensuring no event can be handled until that loop has finished, so any event will only be handled (and the timeout cleared) inbetween the execution of each loop (hence why you sometimes have to wait for 2 or more full executions before interrupting).
The problem is exactly contains in bold words : I want to be sure to interrupt the execution when I want and not to wait 2 or more full executions before interrupting
Second EDIT :
In summary : takes this jsfiddle : http://jsfiddle.net/BwNca/5/ (the code above).
Update this jsfiddle and provide a solution to :
Mouse move on the red div. Then click and continue moving : the right counter must raise only once. But sometimes it raises 2 or 3 times before the first counter can run again... this is the problem, it must raise only once !
The BIG problem here is setTimeout is unpredictable once it started, and especially when it is doing some heavy lifiting.
You can see the demo here:
http://jsfiddle.net/wao20/C9WBg/
var secTmr = setTimeout(function(){
$('#display').append('Timeout Cleared > ');
clearTimeout(secTmr);
// this will always shown
$('#display').append('I\'m still here! ');
}, 100);
There are two things you can do to minimize the impact on the browser performance.
Store all the intances of the setTimeoutID, and loop through it when you want to stop
var timers = []
// When start the worker thread
timers.push( setTimeout(function () { sleep(1000);}, 1) );
// When you try to clear
while (timers.length > 0) {
clearTimeout(timers.pop());
}
Set a flag when you try to stop process and check that flag inside your worker thread just in case clearTimeout failed to stop the timer
// Your flag
var STOPForTheLoveOfGod = false;
// When you try to stop
STOPForTheLoveOfGod = true;
while (timers.length > 0) {
clearTimeout(timers.pop());
}
// Inside the for loop in the sleep function
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if (STOPForTheLoveOfGod) {
break;
}
// ...
}
}
You can try out this new script.
http://jsfiddle.net/wao20/7PPpS/4/
I may have understood the problem but assuming you are trying to block the interface after a click for a minimum of 1 second and unblocking it by moving the mouse (after that 1 second minimum):
This is not a good implementation of sleep, as you are keeping the process running the whole time (doing nothing != sleeping), this results in a waste of resources.
Why not create an overlay (a semi/fully transparent div), put it on top of the rest of the interface (position fixed, full width and full height) and use it to prevent any interaction with the underlying interface. Then destroy it when the conditions are right (a second has passed and the user moved the mouse).
This behaves more like a sleep (has some initial processing time but then releases the processor for a given amount of time) and should help you achieve the behavior you need (assuming i understood it right).
It has the added bonus of allowing you to give the user some visual cue that some processing is being done.
Edit:
Also, by "blocking" the process on your loop you are ensuring no event can be handled until that loop has finished, so any event will only be handled (and the timeout cleared) inbetween the execution of each loop (hence why you sometimes have to wait for 2 or more full executions before interrupting).
Surprising enough you have not figured out that, when you setTimeout(); you can input a check after that. A variable is true then trash the wait, or trash it. Now there is a method that you can check to scroll with a scroll bar. After you have checked it true inside a variabled using the means, then you will find this will repeat inifite times as they scroll the bar, making many executing times of 5 seconds. To fix this add a 1 second wait to make sure it doesn't over repeat. Your welcome :)
Any long-running function is going to tie up your browser window. Consider moving your complexAlgorithm() outside of your main javascript code using WebWorkers.
The answer is in your question
...the refresh must stop and when the scrollbar stops to move, the
refresh occurs again.
You should write complexAlgorithm in such way that you can almost instantly brake it in a middle (just when you know you will have to re run)
so main code should look something like
stopAllRefresh; //should instantly(or after completing small chunk) stop refresh
setTimeout(startRefresh, 100);
and render graph in small chunks (each runs < 1sec) in setTimeout
like
var curentGraph = 0;
var curentChunk = 0;
function renderGraphChunk(){
if (needToBreak) //check if break rendering
{exit};
// Render chunk here
render(curentGraph, curentChunk);
curentChunk +=1;
setTimeout(renderGraphChunk, 1);
}
this is just a idea sketch, real implementation can be completely different
What you want to do can not be done without web worker, that is only implemented in some latest browser specially Chrome.
Otherwise, you have to break your algorithm in queue. Just like jQuery UI puts every next animation calculation in queue. http://api.jquery.com/jQuery.queue/
It is a simple queue and next instruction set is queued with help of setTimeout.
for (i=0; i <1000; i++)
{
process (i) ;
}
Can be translated to
function queue(s,n, f)
{
this.i=s;
this.n=n;
this.f=f;
this.step = function(){
if ( this.i <this.n)
{
this.f(this.i);
this.i = this.i +1;
var t = this;
setTimeout( function ( ) { t.step(); } , 5);
}
}
this.step();
}
queue ( O, 1000, function(i){
process(i);
}) ;
This is just an example of how Synchronous for loop can be written to execute same logic asynchronously using smaller independent iteration.
Try and check out web workers. I think it will be useful in this situation.
http://en.wikipedia.org/wiki/Web_worker
http://www.html5rocks.com/en/tutorials/workers/basics/
I am not too familiar with the specifics of every javascript implementation on each browser. I do know however that using setTimeout, the method passed in gets called on a separate thread. So would using a setTimeout recursively inside of a method cause its stack to grow indefinitely until it causes a Stack Overflow? Or would it create a separate callstack and destroy the current frame once it goes out of focus? Here is the code that I'm wondering about.
function pollServer()
{
$.getJSON("poll.php", {}, function(data){
window.setTimeout(pollServer, 1000);
});
}
window.setTimeout(pollServer, 0);
I want to poll the server every second or so, but do not want to waste CPU cycles with a 'blocking loop' - also I do not want to set a timelimit on how long a user can access a page either before their browser dies.
EDIT
Using firebug, I set a few breakpoints and by viewing the "Script -> Stack" panel saw that the call stack is literally just "pollServer" and it doesn't grow per call. This is good - however, do any other implementations of JS act differently?
I am not sure if it would create a stack overflow, but I suggest you use setInterval if the period is constant.
This is how prototype implements its PeriodicalExecuter.
// Taken from Prototype (www.prototypejs.org)
var PeriodicalExecuter = Class.create({
initialize: function(callback, frequency) {
this.callback = callback;
this.frequency = frequency;
this.currentlyExecuting = false;
this.registerCallback();
},
registerCallback: function() {
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
execute: function() {
this.callback(this);
},
stop: function() {
if (!this.timer) return;
clearInterval(this.timer);
this.timer = null;
},
onTimerEvent: function() {
if (!this.currentlyExecuting) {
try {
this.currentlyExecuting = true;
this.execute();
} finally {
this.currentlyExecuting = false;
}
}
}
});
setTimeout executes sometime later in the future in the event pump loop. Functions passed to setTimeout are not continuations.
If you stop and think about it, what useful purpose or evidencec is there that the call stack is shared by the timeout function.
If they were shared what stack would be shared from the setter to the timeout function ?
Given the setter can do a few returns and pop some frames - what would be passed ?
Does the timeout function block the original thread ?
Does the statement after the setTimeout function execute after the timeout executes ?
Once you answer those questions it clearly becomes evident the answerr is NO.
setTimeout does not grow the callstack, because it returns immediately. As for whether your code will run indefinitely in any browser, I'm not sure, but it seems likely.
take a look at the jQuery "SmartUpdater" plugin.
http://plugins.jquery.com/project/smartupdater
Following features are available:
stop() - to stop updating.
restart() - to start updating after pause with resetting time interval to minTimeout.
continue() - to start updating after pause without resetting time interval.
status attribute - shows current status ( running | stopping | undefined )
updates only if new data is different from the old one.
multiplies time interval each time when data is not changed.
handle ajax failures by stopping to request data after "maxFailedRequests".