I want to make a timer that counts down from some initial number and stops when it gets to zero.
I originally did this with setInterval, but I wanted to separate the timer (setInterval) from the count down function, and was finding it difficult to terminate the setInterval.
I'm currently trying to achieve the same thing with a setTimeout which conditionally calls the same setTimeout again, but it doesn't work.
function Timer(initialTime) {
this.time = initialTime;
this.tickTock = null
}
Timer.prototype.countDown = function() {
if (this.time <= 0) {
clearTimeout(this.tickTock);
} else {
console.log(this.time);
this.time--;
this.proceed();
}
}
Timer.prototype.proceed = function() {
this.tickTock = setTimeout(this.countDown, 3000);
}
var timer = new Timer(10);
timer.proceed();
When calling timer.proceed(), I'm getting error:
TypeError: this.proceed is not a function
at Timer.countDown [as _onTimeout]
How can I refer to the proceed function from within the countDown function?
The callback to setTimeout is not bound to your object but it's bound to window thus this is the window objet not your timer object. You can bind the callback using Function.prototype.bind like this:
this.tickTock = setTimeout(this.countDown.bind(this), 3000);
Note: when using setTimeout there will be no need for this.tickTock, you can stop the counting down by not calling another proceed. You can keep it but it will be of no use. (see the code snippet bellow).
Working code snippet:
function Timer(initialTime) {
this.time = initialTime;
}
Timer.prototype.countDown = function() {
if (this.time <= 0) { // if the counter is less or equal 0, return and don't call proceed
return;
}
// otherwise continue
console.log(this.time);
this.time--;
this.proceed();
}
Timer.prototype.proceed = function() {
setTimeout(this.countDown.bind(this), 1000);
}
var timer = new Timer(10);
timer.proceed();
Related
I think im missing something fairly obvious with how the clearInterval method works.
So with the code below. I would expect the first function call to execute testFunction and set the interval to repeat the function. The 2nd call would execute the second function which will remove the interval from the 1st function. As this would execute far before the 5000ms interval the first function would not be executed again. However it does not behave like this.
Could someone please explain what is wrong with my method?
Reason for this is in a program I am writing I am making repeated get requests, every 30 seconds or so , using setTimeout but i would like a method to easily remove this interval at other points in the program
function testFunction() {
$("#test").append("test");
setTimeout(testFunction, 5000);
}
function stopFunction() {
clearTimeout(testFunction);
}
testFunction();
stopFunction();
setTimeout returns an ID so you should
var timeoutID = setTimeout(blah blah);
clearTimeout(timeoutID);
setTimeout returns an object that you need to pass into the clearTimeout method. See this article for an example: http://www.w3schools.com/jsref/met_win_cleartimeout.asp
setTimeout returns an identifier for the timer. Store this in a variable like:
var timeout;
function testFunction(){
...
timeout = setTimeout(testFunction, 5000);
}
function stopFunction(){
clearTimeout(timeout);
}
Here is a simple and I think better implementation for this .
var _timer = null,
_interval = 5000,
inProgress = false,
failures = 0,
MAX_FAILURES = 3;
function _update() {
// do request here, call _onResolve if no errors , and _onReject if errors
}
function _start() {
inProgress = true;
_update();
_timer = setInterval(_update, _interval);
}
function _end() {
inProgress = false;
clearInterval(_timer);
}
function _onReject(err) {
if (failures >= MAX_FAILURES) {
_end();
return false;
}
_end();
failures++;
_start();
}
function _onResolve(response) {
return true;
}
I've looked at many different solutions to this, none of which worked. I know it has something to do with setTimeout, but I don't know how to implement it properly.
function myfunction()
{
//the function
//wait for 1 second before it can be ran again
}
To clarify: I don't want to call the function at a regular interval, I want to be able to enforce a delay before the function can be called again.
var lastTime = 0;
function myFunction() {
var now = new Date().getTime(); // Time in milliseconds
if (now - lasttime < 1000) {
return;
} else {
lastTime = now;
}
// rest of function
}
You don't need to use setTimeout at all. The following is similar to other answers, but uses a closure to remember the last time the function ran rather than a global variable.
var myFunction = function() {
var lastTime = new Date();
return function() {
var now = new Date();
if ((now - lastTime) < 1000) return;
lastTime = now;
/* do stuff */
};
}());
I think the easiest solution would be to hold a boolean variable and reset it to true after a given delay.
fiddle
HTML
<button id="clickme">click me!</button>
JavaScript
var canGo = true,
delay = 1000; // one second
var myFunction = function () {
if (canGo) {
canGo = false;
// do whatever you want
alert("Hi!");
setTimeout(function () {
canGo = true;
}, delay)
} else {
alert("Can't go!");
}
}
$("#clickme").click(function(){
myFunction();
})
With this, you hold a boolean, canGo, and set it to true. If the function is run, it sets canGo to false and sets a setTimeout() for a time period of delay, in milliseconds. If you try to run the function again, it won't run and will, instead, alert("Can't go!"). This was just for demonstrative purposes; you don't need that part. After delay, canGo will be set to true, and you will be able to once more run the function.
var lastRan = 0;
var myFunction = function() {
var now = Date.now();
if(now-lastRan < 1000) {
return;
}
lastRan = now;
//rest of function
};
You may want to use throttle or debounce from underscore.js
http://underscorejs.org/#throttle
throttle_.throttle(function, wait, [options])
Creates and returns a
new, throttled version of the passed function, that, when invoked
repeatedly, will only actually call the original function at most once
per every wait milliseconds. Useful for rate-limiting events that
occur faster than you can keep up with.
By default, throttle will execute the function as soon as you call it
for the first time, and, if you call it again any number of times
during the wait period, as soon as that period is over. If you'd like
to disable the leading-edge call, pass {leading: false}, and if you'd
like to disable the execution on the trailing-edge, pass {trailing:
false}.
var throttled = _.throttle(updatePosition, 100);
$(window).scroll(throttled);
http://underscorejs.org/#debounce
debounce_.debounce(function, wait, [immediate])
Creates and returns a
new debounced version of the passed function which will postpone its
execution until after wait milliseconds have elapsed since the last
time it was invoked. Useful for implementing behavior that should only
happen after the input has stopped arriving. For example: rendering a
preview of a Markdown comment, recalculating a layout after the window
has stopped being resized, and so on.
Pass true for the immediate parameter to cause debounce to trigger the
function on the leading instead of the trailing edge of the wait
interval. Useful in circumstances like preventing accidental
double-clicks on a "submit" button from firing a second time.
var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);
If you just want to run your function again after a set time, you can use setTimeout and pass it the function to run and the delay period in milliseconds.
function myfunction() {
//the function
//run again in one second
setTimeout(myfunction, 1000);
}
Edited based on poster's comments:
var waiting = false;
var myfunction = function() {
if (!waiting) {
//Run some code
waiting = setTimeout(function() {
waiting = false;
}, 1000);
}
};
I've been playing around with a site, in which I want to continue clicking a button for i amount of times every interval seconds.
My code is:
clickbidBtn1 = function() {
var bidBtn=document.getElementById("BidButton");
var interval = 15000;
for (var i=3; i>=0; i--){
setTimeout(bidBtn.click(1);,i*interval);
};
I've found out that GM executes all i amount of clicks at the same time, not with the intended delay. is there a way to delay the time of click? Say i wanted the function to click the button every 15 second for i amount of times.
I was thinking of giving it some more variables, and adding one variable in the settimeout code part, which only executes # the click, then comparing increased variables with current ones before going to the next settimeout... but haven't thought it through yet... it seems to be a complicated process for a simple process... :( i wll play around with it a bit
Use setInterval() for this.
One way:
var bidClickTimer = 0;
var numBidClicks = 0;
function clickbidBtn1 ()
{
var interval = 15000;
bidClickTimer = setInterval (function() {BidClick (); }, interval);
}
function BidClick ()
{
numBidClicks++;
if (numBidClicks > 3)
{
clearInterval (bidClickTimer);
bidClickTimer = "";
}
else
{
bidBtn.click (1);
}
}
clickbidBtn1 ();
Alternatively, without using global vars:
function clickbidBtn1 ()
{
var interval = 15000;
this.numBidClicks = 0;
this.bidClickTimer = 0;
this.BidClick = function () {
numBidClicks++;
if (numBidClicks > 3)
{
clearInterval (bidClickTimer);
bidClickTimer = "";
}
else
{
bidBtn.click (1);
}
};
this.bidClickTimer = setInterval (function(thisScope) {thisScope.BidClick (); }, interval, this);
}
clickbidBtn1 ();
Just to explain why your code does not work: You are calling the .click method immediately (putting () after a function name calls the function) and actually passing the return value of that function to setTimeout. The for loop is so fast that everything seem to happen at the same time.
You have to pass a function reference to setTimeout, e.g. an anonymous function:
setTimeout(function() {
bidBtn.click(1);
}, i*interval);
The window.setTimeout (and related setInterval) function in Javascript allows you to schedule a function to be executed sometime in the future:
id = setTimeout(function, delay);
where "delay" is the number of milliseconds into the future at which you want to have the function called. Before this time elapses, you can cancel the timer using:
clearTimeout(id);
What I want is to update the timer. I want to be able to advance or retard a timer so that the function gets called x milliseconds sooner or later than originally scheduled.
If there were a getTimeout method, you could do something like:
originally_scheduled_time = getTimeout(id);
updateTimeout(id, originally_schedule_time + new_delay); // change the time
but as far as I can tell there's nothing like getTimeout or any way to update an existing timer.
Is there a way to access the list of scheduled alarms and modify them?
Is there a better approach?
thanks!
If you really want this sort of functionality, you're going to need to write it yourself.
You could create a wrapper for the setTimeout call, that will return an object you can use to "postpone" the timer:
function setAdvancedTimer(f, delay) {
var obj = {
firetime: delay + (+new Date()), // the extra + turns the date into an int
called: false,
canceled: false,
callback: f
};
// this function will set obj.called, and then call the function whenever
// the timeout eventually fires.
var callfunc = function() { obj.called = true; f(); };
// calling .extend(1000) will add 1000ms to the time and reset the timeout.
// also, calling .extend(-1000) will remove 1000ms, setting timer to 0ms if needed
obj.extend = function(ms) {
// break early if it already fired
if (obj.called || obj.canceled) return false;
// clear old timer, calculate new timer
clearTimeout(obj.timeout);
obj.firetime += ms;
var newDelay = obj.firetime - new Date(); // figure out new ms
if (newDelay < 0) newDelay = 0;
obj.timeout = setTimeout(callfunc, newDelay);
return obj;
};
// Cancel the timer...
obj.cancel = function() {
obj.canceled = true;
clearTimeout(obj.timeout);
};
// call the initial timer...
obj.timeout = setTimeout(callfunc, delay);
// return our object with the helper functions....
return obj;
}
var d = +new Date();
var timer = setAdvancedTimer(function() { alert('test'+ (+new Date() - d)); }, 1000);
timer.extend(1000);
// should alert about 2000ms later
I believe not. A better approach might be to write your own wrapper which stores your timers (func-ref, delay, and timestamp). That way you can pretend to update a timer by clearing it and calculate a copy with an updated delay.
Another wrapper:
function SpecialTimeout(fn, ms) {
this.ms = ms;
this.fn = fn;
this.timer = null;
this.init();
}
SpecialTimeout.prototype.init = function() {
this.cancel();
this.timer = setTimeout(this.fn, this.ms);
return this;
};
SpecialTimeout.prototype.change = function(ms) {
this.ms += ms;
this.init();
return this;
};
SpecialTimeout.prototype.cancel = function() {
if ( this.timer !== null ) {
clearTimeout(this.timer);
this.timer = null;
}
return this;
};
Usage:
var myTimer = new SpecialTimeout(function(){/*...*/}, 10000);
myTimer.change(-5000); // Retard by five seconds
myTimer.change(5000); // Extend by five seconds
myTimer.cancel(); // Cancel
myTimer.init(); // Restart
myTimer.change(1000).init(); // Chain!
It may be not exactly what you want, but take a look anyway, maybe you can use it to your benefit.
There is a great solution written by my ex-coworker that can create special handler functions that can stop and start timeouts when required. It is most widely used when you need to create a small delay for hover events. Like when you want to hide a mouseover menu not exactly at the time when a mouse leaves it, but a few milliseconds later. But if a mouse comes back, you need to cancel the timeout.
The solution is a function called getDelayedHandlers. For example you have a function that shows and hides a menu
function handleMenu(show) {
if (show) {
// This part shows the menu
} else {
// This part hides the menu
}
}
You can then create delayed handlers for it by doing so:
var handlers = handleMenu.getDelayedHandlers({
in: 200, // 'in' timeout
out: 300, // 'out' timeout
});
handlers becomes an object that contains two handler functions that when being called cancel the other one's timeout.
var element = $('menu_element');
element.observe('mouseover', handlers.over);
element.observe('mouseout', handlers.out);
P.S. For this solution to work you need to extend the Function object with the curry function, which is automatically done in Prototype.
One possibility can be like this:
if (this condition true)
{
setTimeout(function, 5000);
}
elseif (this condition true)
{
setTimeout(function, 10000);
}
else
{
setTimeout(function, 1000);
}
It's up to your how you construct your conditions or the logic. thanks
I use the following code to create countdowns in Javascript. n is the number of times to repeat, freq is the number of milliseconds to wait before executing, funN is a function to call on each iteration (typically a function that updates part of the DOM) and funDone is the function to call when the countdown is complete.
function timer(n, freq, funN, funDone)
{
if(n == 0){
funDone();
}else{
setTimeout(function(){funN(n-1); timer(n-1, freq, funN, funDone);}, freq);
}
}
It can be called like so:
timer(10,
1000, /* 1 second */
function(n){console.log("(A) Counting: "+n);},
function() {console.log("(A) Done!");}
);
timer(10,
500,
function(n){console.log("(B) Counting: "+n);},
function() {console.log("(B) Done!");}
);
The advantage of this is that I can call timer() as many times as I want without worrying about global variables etc. Is there a better way to do this? Is there a clean way to make setInterval stop after a certain number of calls (without using global variables)? This code also creates a new lambda function with each call to setTimeout which seems like it could be problematic for large countdowns (I'm not sure how javascript's garbage collector handles this).
Is there a better way to do this? Thanks.
This is basically the same idea as #balabaster, but it is tested, uses prototype, and has a little more flexible interface.
var CountDownTimer = function(callback,n,interval) {
this.initialize(callback,n,interval);
}
CountDownTimer.prototype = {
_times : 0,
_interval: 1000,
_callback: null,
constructor: CountDownTimer,
initialize: function(callback,n,interval) {
this._callback = callback;
this.setTimes(n);
this.setInterval(interval);
},
setTimes: function(n) {
if (n)
this._times = n
else
this._times = 0;
},
setInterval: function(interval) {
if (interval)
this._interval = interval
else
this._interval = 1000;
},
start: function() {
this._handleExpiration(this,this._times);
},
_handleExpiration: function(timer,counter) {
if (counter > 0) {
if (timer._callback) timer._callback(counter);
setTimeout( function() {
timer._handleExpiration(timer,counter-1);
},
timer._interval
);
}
}
};
var timer = new CountDownTimer(function(i) { alert(i); },10);
...
<input type='button' value='Start Timer' onclick='timer.start();' />
I'd create an object that receives a counter and receives a function pointer to execute, something akin to the following pseudo code:
TimedIteration = function(interval, iterations, methodToRun, completedMethod){
var counter = iterations;
var timerElapsed = methodToRun; //Link to timedMethod() method
var completed = callbackMethod;
onTimerElapsed = function(){
if (timerElapsed != null)
timerElapsed();
}
onComplete = function(){
if (completed != null)
completed();
}
timedMethod = function(){
if (counter != null)
if (counter > 0) {
setTimeOut(interval, onTimerElapsed);
counter--;
}
else
onComplete();
this = null;
}
}
if ((counter != null)&&(counter > 0)){
//Trip the initial iteration...
setTimeOut(interval, timedMethod);
counter--;
}
}
obviously this is pseudo code, I've not tested it in an IDE and syntactically I'm not sure if it'll work as is [I'd be astonished if it does], but basically what you're doing is you're creating a wrapper object that receives a time interval, a number of iterations and a method to run upon the timer elapsed.
You'd then call this on your method to run like so:
function myMethod(){
doSomething();
}
function doWhenComplete(){
doSomethingElse();
}
new TimedIteration(1000, 10, myMethod, doWhenComplete);
I like your original solution better than the proposed alternatives, so I just changed it to not create a new function for every iteration (and the argument of fun() is now the value before decrement - change if needed...)
function timer(n, delay, fun, callback) {
setTimeout(
function() {
fun(n);
if(n-- > 0) setTimeout(arguments.callee, delay);
else if(callback) callback();
},
delay);
}