How to stop/override a Jquery TimeOut function? - javascript

I have a small jquery snippet that displays notification message at the top of the screen in response to user actions on a page. The notification is often displayed after Ajax actions with dynamic content inside it.
For example:
$("#mini-txt").html("Thank you!");
$("#mini").fadeIn("fast");
setTimeout(function() {$("#mini").animate({height: "hide", opacity: "hide"}, "medium");}, 3000);
The notification works well, except when a user does two or more actions in rapid succession, in which case the TimeOut function will confuse itself and the second message appears to come inside the previous 3000 milliseconds.
Is there a way to "kill" the previous notification if a new action is performed. I've got no problem with the actions/selectors, just the TimeOut function.... either stopping it or overriding it somehow. Or perhaps there's a better alternative for getting the message to linger on the screen for a few seconds before disappearing?
Thank you.

First, you store the return value for the setTimeout function:
// Set the timeout
var timeout = setTimeout(function()
{
// Your function here
}, 2000);
Then, when you're ready to kill the timeout...you just call clearTimeout with the stored value from the previous call to setTimeout.
// Then clearn the timeout
clearTimeout(timeout);

You can use .stop()
Stop the currently-running animation
on the matched elements.

jQuery 1.4 has a built in method to handle delays for animations you can do something like this:
$("#mini-txt").html("Thank you!");
$("#mini").fadeIn("fast").delay(3000).animate({height: "hide", opacity: "hide"}, "medium");
And then later when you want to clean the animation queue you can do:
$("#mini").stop(true);

This will clear timeout after function run with delay 500ms
var timeout = setTimeout(function(){
/* YOUR FUNCTION */
}, 500, function(){
clearTimeout(timeout);
});

Related

Javascript Deep thinking: Recursive setTimeOut function with a clearTimeout call on a button pressed?

I have a quick question about using recursive setTimeOut recursively and a clearTimeOut that get called somewhere else.
On rare cases, will there ever gonna be a bug where clearTimeOut doesn't actually stop the loop? Is it possible that the timeOutID get changes into a new value and clearTimeout is called on the old value?
Here is the code:
timeOutID = 0;
function timeOutRecusive() {
timeOutID = setTimeout('timeOutRecusive();', 1000);
}
function killTimeOutRecusive() {
clearTimeout(timeOutID);
}
//when page started.
start() {
timeOutRecusive();
}
//When a button is press, calls killTimeOutRecursive();
EDIT: I have some typo in my code. It should be 'timeOutID' instead of clockID. clearTimeOut should be 'clearTimeout' (using its built-in)
This approach is pretty bullet-proof, and a standard practice.
Is it possible that the timeoutId get changes into a new value and clearTimeout is called on the old value?
No, this is not possible. JS code doesn't run in parallel, there are no data races from multithreading.
The only edge case where killTimeoutRecursive does not work as expected is when it is called from within timeoutRecursive, after the old timeout occurred and before the new one was created:
var timeoutId = 0;
function timeoutRecusive() {
callback();
timeoutId = setTimeout(timeOutRecusive, 1000);
}
function killTimeoutRecusive() {
clearTimeout(timeoutId);
}
function callback() { // this might be user-provided
killTimeoutRecursive();
}
Your thought is legit. If the callback method of the specified timeout would be called in a parallel execution, it could just create a new timeout (not yet updated the variable) while you try to clear the current timeout.
However, the timeout handling is executed sequential. (thats why it some times can take way longer than 1000ms for the callback to be fired)
Meaning:
-If your code is just about to create a new timeout, your clear call "waits" and then clears the 3ms old timer.
-If you are just about to clear the timeout, when 1000 ms have elapsed, the callback will not be fired, as long as your code is busy. And when its cleared, it wont be added to the event queue anymore, when the timeout is executed after delayed 1004ms.
No.
Ignoring the fact there is no clearTimeOut function (it's clearTimeout) and it's being called with clockID, not timeOutID), all of these statements will be run sequentially; any tasks that setTimeout and friends might run will be only run after the current synchronous block of JavaScript is run, i.e. the sequence would be something like
[frame]
start()
setTimeout(...)
clearTimeout(...)
[frame]
(this is where timeout functions could be run)

Calling JavaScript Function that has SetTimeout Assigned to It

I'm not 100% sure how setTimeout works in JavaScript. Say I have something like this:
$(document).ready(function() {
testTimeout();
});
function testTimeout() {
alert("testing timeout");
setTimeout(testTimeout, 5000);
}
This would display a popup window every 5 after the page is ready. What would happen if then I called testTimeout from a button click?
$("#button").click(function() {
testTimeout();
});
Would the button click call testTimeout and add another timeout every 5 seconds? Or, would the button click reset the timeout from when the button was pressed? The reason I am asking is because I would like to design something like this where I can pass a parameter to my timeout function. When the web page starts up, I have a default parameter. However, if I press a button, I would like my timeout function to be called right away and every 5 seconds after with my new parameter. But, I don't want the timeout function with the old parameter to continue repeating. How can I achieve this? Any help and understanding would be greatly appreciated.
This would display a popup window every 5 after the page is ready.
No it wouldn't, it would show an alert repeatedly with no delay and/or cause a "too much recursion" error, because setTimeout(testTimeout(), 5000) calls testTimeout and passes its return value into setTimeout, just like foo(bar()) calls bar and passes its return value into foo.
If you remove the ():
function testTimeout() {
alert("testing timeout");
setTimeout(testTimeout, 5000);
// here --------------^
}
Then it would do that.
What would happen if then I called testTimeout from a button click?
You'd end up with the function being called twice as often (more than once every 5 seconds), because every time you call it, it reschedules itself. A third time would make it more frequently still (three times/second), and so on.
If you want to avoid that, one option is to remember the timer handle and cancel any outstanding timed callback if you call the function before then:
var handle = 0;
function testTimeout() {
clearTimeout(handle); // Clears the timed call if we're being called beforehand
alert("testing timeout");
handle = setTimeout(testTimeout, 5000);
}
(I initialized handle with 0 because calling clearTimeout with a 0 is a no-op.)
Have you tried to asign variable to your setinterval;
var foo = setTimeout(testTimeout(), 5000);
and then when right event comes just destroy that variable.
clearInterval(foo);
And now you can asign it again...
In your case it would simply repeat endlessly, because you're executing the function instead of passing the reference. You should do it like this:
function testTimeout() {
alert("testing timeout)";
setTimeout(testTimeout, 5000);
}
Note the missing braces after testTimeout. This tells setTimeout to execute that function, instead of the result of that function, which is how your original code behaved.
" I would like my timeout function to be called right away and every 5 seconds after with my new parameter. But, I don't want the timeout function with the old parameter to continue repeating "
In order to achieve what you're trying to do you should remove the timeout:
var timeoutId;
function testTimeout() {
alert("testing timeout)";
clearTimeout(timeoutId );
timeoutId = setTimeout(testTimeout, 5000);
}
Notes:
You can stop the previous timeoutI from firing by catching the id returned from the setTimeout method and passing that to the clearTimeout method

Set minimum time and timeout

I have loading animation which displays while the webpage is loading in the background. Once loading is complete it disappears.
What I'd like...
1. The animation to appear for a minimum of 1 second.
2. Once the 1 second minimum has expired, the animation will be naturally removed following page loading.
3. To avoid the possibility of a fault (and watching an endless animation loop), I want the animation to time out if it takes more than 5 seconds to load.
Here's what I have so far...
$(window).load(function()
{
$('#followingBallsG').hide();
$('#backgroundcolor').hide();
});
Any assistance you can provide would be greatly appreciated. Thanks.
There are no built-in jQuery features for this. You will need to write out the logic for this. Here is a simple implementation, free of race-conditions.
(function(){
var didDone = false;
function done() {
//Prevent multiple done calls.
if(!didDone)
{
didDone = true;
//Loading completion functionality here.
$('#followingBallsG').hide();
$('#backgroundcolor').hide();
}
}
//Variables to keep track of state.
var loaded = false;
var minDone = false;
//The minimum timeout.
setTimeout(function(){
mindone = true;
//If loaded, fire the done callback.
if(loaded)
{
done();
}
}, 1000);
//The maximum timeout.
setTimeout(function(){
//Max timeout fire done.
done();
}, 5000);
//Bind the load listener.
$(window).load(function(){
loaded = true;
//If minimum timeout done, fire the done callback.
if(minDone)
{
done();
}
});
})();
I've wrapped it in a immediately invoked function expression and assumed $(window).load is the event you are listening for. It should be easy to adapt this to another event or to run at another time if this is not the desired effect.

Loading image while JavaScript function(s) run

is there a way to show a loading image while a JavaScript function is running. I have one that takes about 2-5 seconds, would be nice if i could have something like the jQuery-ajax function
$("#loading").bind("ajaxStart", function(){
$(this).attr("style", "visibility: visible")
}).bind("ajaxStop", function(){
$(this).attr("style", "visibility: hidden")
});
clarification edit:
The idea is that every time a JavaScript function runs and takes over, say 3/4 of a second, the loading image would be displayed. It really has nothing to do with this ajax function, just the same principle of always catching a running JavaScript and timing it.
Thanks!
Well then... After you commented, that changes everything.
You cannot have it automatically show when any javascript runs because there is no real hook for that. You can however leverage jquery custom events through the use of .trigger() and .bind() using your own custom event.
function myFunctionThatDoesSomething() {
$('body').trigger('Custom.BeginWork');
// Do Something ...
$('body').trigger('Custom.EndWork');
}
Although long running operations should probably be done asynchronously so they don't block on the event:
$("#Something").click(function() {
$('body').trigger('Custom.BeginWork');
setTimeout(function() { DoWorkFunction(/* you can pass params here if you need */); }, 0);
// Causes it to be executed in the background 0ms from now
});
function DoWorkFunction() {
// Stuff...
$('body').trigger('Custom.EndWork');
}
Then register a .bind() event much like .ajaxStart() and .ajaxStop()
$('#Working').bind('Custom.StartWork', function() {
$(this).show();
});
$('#Working').bind('Custom.EndWork', function() {
$(this).hide();
});
Here is a working jsFiddle Example.
Update:
In your jsFiddle you've done a double setTimeout. Here:
setTimeout(function() {
// Call Operation Here
try { setTimeout(function () { LongRunningOperation(10000); }, 1000);
}
finally
{
$("body").trigger('Custom.End');
}
}, 50); // 50ms delay because some browsers *cough*IE*cough* are slow at rendering dom changes
What this translates to is:
So the Custom.End event is getting fired after scheduling the long running function to run, not when it completes. setTimeout is asynchronous and non-blocking.

In jQuery Form, 'success' is being called before 'beforeSubmit' is finished

I'm using the jQuery Form plugin to upload an image. I've assigned a fade animation to happen the beforeSubmit callback, but as I'm running locally, it doesn't have time to finish before the success function is called.
I am using a callback function in my fade(); call to make sure that one fade completes, before the next one begins, but that does not seem to guarantee that the function that's calling it is finished.
Am I doing something wrong? Shouldn't beforeSubmit complete before the ajax call is submitted?
Here's are the two callbacks:
beforeSubmit:
function prepImageArea() {
if (userImage) {
userImage.fadeOut(1500, function() {
ajaxSpinner.fadeIn(1500);
});
}
}
success:
function imageUploaded(data) {
var data = evalJson(data);
userImage.attr('src', data.large_thumb);
ajaxSpinner.fadeOut(1500, function() {
userImage.fadeIn(1500);
});
}
I think you may be getting too fancy with those fade animations :)... In the beforeSubmit the fadeOut is setup but the function returns immediately causing the submit to happen. I guess the upload is happening under 3 seconds causing the new image to appear before your animations are complete.
So if you really really want this effect, then you will need to do the image fadeout, spinner fadein, and once that is complete triggering the upload. Something like this:
if (userImage) {
userImage.fadeOut(1500, function() {
ajaxSpinner.fadeIn(1500, function(){
//now trigger the upload and you don't need the before submit anymore
});
});
}
else {
// trigger the upload right away
}
Even though the beforeSubmit callback is called before submitting the form, the userImage.fadeOut function is synchronous (i.e. it spawns a separate thread of execution to execute the fade animation then it continues execution) and it returns immediately. The fade animation takes 1.5 seconds to complete and as you are running on localhost the ajax response is returned faster than 1.5 seconds and thus you won't see the animation, in real world applications it mostly unlikely that ajax requests would take less than 1.5 seconds, so you are good :)

Categories

Resources