Emberjs Countdown - not stoppable - javascript

Heyho
I have a little issue with my countdown written in Ember. More precisely in stopping my counter when it hits 0.
First of all... I'm using
Ember Version
DEBUG: Ember : 1.12.0
I've created a 'service' Class with some simple methods to handle the countdown process.
interval: function() {
return 10; // Time between polls (in ms)
}.property().readOnly(),
totalTime: function() {
return 5000; // Total Time (in ms)
}.property(),
timeDiff: 0,
timeLeft: function() {
return Math.floor((this.get('totalTime') - this.get('timeDiff')) / 1000);
}.property('timeDiff'),
hasFinished: function() {
return this.get('timeLeft') === 0;
}.property('timeLeft'),
// Schedules the function `f` to be executed every `interval` time.
schedule: function(f) {
return Ember.run.later(this, function() {
f.apply(this);
this.set('timer', this.schedule(f));
}, this.get('interval'));
},
// Starts the countdown, i.e. executes the `onTick` function every interval.
start: function() {
this.set('startedAt', new Date());
this.set('timer', this.schedule(this.get('onTick')));
},
// Stops the countdown
stop: function() {
Ember.run.cancel(this.get('timer'));
},
onTick: function() {
let self = this;
self.set('timeDiff', new Date() - self.get('startedAt'));
if (self.get('hasFinished')) {
// TODO: Broken - This should stop the countdown :/
self.stop();
}
}
CountDown with Ember.run.later()
I'm starting the countdown within my controller (play action).
The countdown counts down as it should but it just doesn't stop :(
The self.stop() call in onTick() just doesn't do anything at all...
I tried to stop the countdown with an other action in my controller and that is working as it should :/
Any ideas how to solve that problem??
Cheers Michael

I've taken the courtesy or writing a Countdown service based on the code you have provided that allows you to start, reset and stop the countdown. My code assumes you are using Ember CLI, but I have included a JSBin that takes older ES5 syntax into account.
app/services/countdown.js
import Ember from 'ember';
const { get, set, computed, run } = Ember;
export default Ember.Service.extend({
init() {
set(this, 'totalTime', 10000);
set(this, 'tickInterval', 100);
set(this, 'timer', null);
this.reset();
},
remainingTime: computed('elapsedTime', function() {
const remainingTime = get(this, 'totalTime') - get(this, 'elapsedTime');
return (remainingTime > 0) ? remainingTime : 0;
}),
hasFinished: computed('remainingTime', function() {
return get(this, 'remainingTime') === 0;
}),
reset() {
set(this, 'elapsedTime', 0);
set(this, 'currentTime', Date.now());
},
start() {
this.stop();
set(this, 'currentTime', Date.now());
this.tick();
},
stop() {
const timer = get(this, 'timer');
if (timer) {
run.cancel(timer);
set(this, 'timer', null);
}
},
tick() {
if (get(this, 'hasFinished')) {
return;
}
const tickInterval = get(this, 'tickInterval');
const currentTime = get(this, 'currentTime');
const elapsedTime = get(this, 'elapsedTime');
const now = Date.now();
set(this, 'elapsedTime', elapsedTime + (now - currentTime));
set(this, 'currentTime', now);
set(this, 'timer', run.later(this, this.tick, tickInterval));
}
});
I've made an example of this implementation available on JSBin for you to toy around with.

Related

How to kill and restart the recursive function in javascript

I am working on knockout js.
In that i have a recursive function which executes a function every minute. for that am using a timer every 60 sec it will execute also same will be reflecting in the UI also.
In my case, if i try to assign or initialize a timer value(observable) which is inside a loop, it doesn't reflecting instead of reflecting it is added to the pipeline and that much time loop is running simultaneously.
In that case i want to kill the loop and again want to restart every time i am changing the timer value.
timerInSec=60;
var loop = function () {
if (this.timer() < 1) {
myFunction()
this.timer(this.timerInSec - 1);
setTimeout(loop, 1000);
} else {
this.timer(this.timer() - 1);
setTimeout(loop, 1000);
}
};
loop();
Here is my solution. Please check.
timerInSec = 60;
const Loop = (function () {
let timer = 0;
let timerId = -1;
const myFunction = function () {
console.log('finished');
}
const fnLog = function (tm) {
console.log('current time = ', tm);
}
const fnProc = function () {
timerId = setTimeout(myFunction, 1000 * timer);
}
return {
start: function (tm = 60) {
this.stop();
timer = tm;
fnProc();
},
stop: function () {
if (timerId !== -1) {
clearTimeout(timerId);
timerId = -1;
}
}
}
})();
Loop.start(timerInSec);
setTimeout(() => {
Loop.start(timerInSec);
}, 500);

getting timer to stop w/js or jq

I'm trying to get my clock to stop at zero and then show results pg. But for now I am unable to get it to stop.
var clock = {
time: 2,
timeleft: 0,
bigben: null,
countDown: function() {
clock.time--;
$("#timer").html(clock.time);
},
start: function() {
bigben = setInterval(clock.countDown, 1000);
},
stop: function() {
// clearInterval(intervalId);
if (time == timeleft) {
window.clearInterval(bigben);
// Display countdown
$('#timer').html(time + "seconds");
time--
}
//end of timer
if (time === timeleft) {
window.clearInterval(bigben);
gameOver();
};
},
You can use setTimeout function. This fonction play his callback when time is at 0.
setTimeout (function () { your code; }, 1000);
1000 is the time in milliseconds
I did not see anywhere you call the stop function.
add clock.stop in your countdown function.
and it seems that in your stop block, the "time", "timeleft" and "bigben" variables are all not reachable....
You need to use clock.time, clock.timeleft, clock.bigben instead.
Try below.
var clock = {
time: 2,
timeleft: 0,
bigben: null,
countDown: function() {
clock.time--;
$("#timer").html(clock.time);
clock.stop();
},
start: function() {
clock.bigben = setInterval(clock.countDown, 1000);
},
stop: function() {
if (clock.time > clock.timeleft) {
$('#timer').html(clock.time + "seconds");
}
if (clock.time === clock.timeleft) {
window.clearInterval(clock.bigben);
//gameOver();
}
}
};
clock.start();
I commented out the gameOver() call, since there is no definition of that function within current code.

Closure in timer countdown javascript

I'm using the countdown timer from here: The simplest possible JavaScript countdown timer?
I'm adding the reset, pause and resume functionalities to the code.
//=================== Timer class ==============================
function CountDownTimer(duration, granularity) {
this.duration = duration
this.granularity = granularity || 1000;
this.tickFtns = [];
this.running = false;
this.resetFlag = false;
}
CountDownTimer.prototype.start = function() {
console.log('calling start');
if (this.running) {
return;
}
this.running = true;
var start = Date.now(),
that = this,
diff, obj,
timeoutID;
(function timer() {
diff = that.duration - (((Date.now() - start) / 1000) | 0);
if (that.resetFlag) {
console.log('Reset inside closure');
clearTimeout(timeoutID);
diff = 0;
that.resetFlag = false;
that.running = false;
return;
}
console.log(diff);
if (diff > 0) {
timeoutID = setTimeout(timer, that.granularity);
} else {
diff = 0;
that.running = false;
}
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
}());
};
CountDownTimer.prototype.onTick = function(ftn) {
if (typeof ftn === 'function') {
this.tickFtns.push(ftn);
}
return this;
};
CountDownTimer.prototype.expired = function() {
return !this.running;
};
CountDownTimer.prototype.setTime = function(secs) {
this.duration = secs;
}
CountDownTimer.prototype.reset = function() {
this.resetFlag = true;
}
CountDownTimer.parse = function(seconds) {
return {
'minutes': (seconds / 60) | 0,
'seconds': (seconds % 60) | 0
};
};
window.onload = function () {
timer = new CountDownTimer(25);
timer.start();
$('#button').click(function() {
console.log("before reset");
console.log(timer);
console.log("after reset");
timer.reset();
console.log(timer);
// timer.setTime(10);
timer.start();
})
}
HTML for testing, check the output at console.
<script src="main.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<button id='button'> </button>
1) Is function timer() in start a closure?
2) I added a resetFlag, the reset method, and the check for resetFlag in the start function. I'm able to stop the timer immediately, but can't start it after that. How do I fix the error?
25
24
23
main.js:64 Reset inside closure
(it supposed to countdown from 25 to 0, and when I press #button, the timer reset and should count from 10 to 0.
================================EDITS==========================================:
After adding running = false, it's still not working.
before reset
main.js:128 CountDownTimer {duration: 25, granularity: 1000, tickFtns: Array[0], running: true, resetFlag: false}
main.js:129 after reset
main.js:131 CountDownTimer {duration: 25, granularity: 1000, tickFtns: Array[0], running: true, resetFlag: true}
calling start
main.js:64 Reset inside closure
It seems that there's some lag after resetting the timer? (The reset inside closure suppposed to appear before the after reset.

setInterval(function(),time) change time on runtime

I want to change setInterval function time when my code is running.
I try this
<script type="text/javascript">
$(function () {
var timer;
function come() { alert("here"); }
timer = setInterval(come, 0);
clearInterval(timer);
timer = setInterval(come, 10000);
});
</script>
First SetInterval does not work!
You're clearing the interval on the next line, so the first one wont work, as it gets cleared right away :
timer = setInterval(come, 0);
clearInterval(timer);
timer = setInterval(come, 10000);
Also, as gdoron says, setting a interval of nothing isn't really valid, and not a really good idea either, use setTimeout instead, or just run the function outright if no delay is needed.
come();
clearInterval(timer);
timer = setInterval(come, 10000);
You can't. You will need to use setTimeout, and call it repetitively:
var timer; // current timeout id to clear
function come(){ /* do something */};
var time; // dynamic interval
(function repeat() {
come();
timer = setTimeout(repeat, time);
})();
With this you can set a different "interval" to be applied each time the function repeat is executed. Yet, nothing changes if alter time during a timeout, you'd need to stop the timeout for that.
I know this is an old post, but I have implemented a typescript version for changing the interval in run time:
class LoopTimer {
private timer: null | NodeJS.Timer;
private callback: (...args: any[]) => void;
private ms: number;
private started: boolean;
constructor(callback: (...args: any[]) => void, ms: number) {
this.callback = callback;
this.ms = ms;
this.timer = null;
this.started = false;
}
start() {
if (!this.started) {
this.timer = setInterval(this.callback, this.ms);
this.started = true;
}
}
stop() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
this.started = false;
}
}
get getStarted(): boolean {
return this.started;
}
setInterval(ms: number) {
this.ms = ms;
if (this.started) {
this.stop();
this.start();
}
}
}
You can use it like this:
The timer will stop and start again when interval is changed.
const myTimer = new LoopTimer(()=>{
console.log("Hello");
}, 100);
myTimer.setInterval(500);
There is no way to directly change the interval at which a function fires. The best you can do is cancel an interval and set a new one with the same function and updated timer. Here's a possible way of doing it:
timer = {
timers:{},
inc:0,
start:function(cb,gap) {
var key = inc;
inc++;
timer.timers[key] = [setInterval(cb,gap),cb];
return key;
},
stop:function(id) {
if( !timer.timers[id]) return;
clearInterval(timer.timers[id][0]);
delete timer.timers[id];
},
change:function(id,newgap) {
if( !timer.timers[id]) return;
clearInterval(timer.timers[id][0]);
setInterval(timer.timers[id][1],newgap);
}
};
Usage:
var myTimer = timer.start(function() {....},1000);
// calls every second
timer.change(myTimer,5000);
// now calls every five seconds
timer = setInterval(come, 0); // zero isn't a valid interval...
You probably wants:
come();
timer = setInterval(come, 10000);
docs on MDN:
delay is the number of milliseconds (thousandths of a second) that the setInterval() function should wait before each call to func. As with setTimeout, there is a minimum delay enforced.
And:
Historically browsers implement setTimeout() "clamping": successive setTimeout() calls with delay smaller than the "minimum delay" limit are forced to the use at least the minimum delay. The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5ms.
this is mi example, i think is more simple and easy to understand
const timer = {
time: 5, // 5 time in seconds
_time: null,
_timeout: null,
fun: () => {},
start() {
if (this._timeout == null) {
const self = this;
this.fun();
this._timeout = setTimeout(function repeater() {
self.fun();
self._timeout = setTimeout(repeater, 1000 * self.time);
}, 1000 * this.time);
}
},
stop() {
const timeout = this._timeout;
this._timeout = null;
this.set_time(); // set time to default
clearTimeout(timeout);
},
set_time(time) {
if (this._time == null) this._time = this.time;
if (time) {
this.time = time;
} else {
this.time = this._time;
}
},
};
Explication:
time: is the time of interval between every iteration, cycle or next call
_time: this variable save the default value of time, when use stop(), this variable(_time) restore "time"
start: this function start the iteration, if you call again, it will not duplicate.
stop: this function, stop the timeout and set default time and _timeout
set_time: this function set a new value to time, if you not send a parameter, time restore to default value, declare on running in this example is "5"
example:
const timer = {
time: 5, // 5 time in seconds
_time: null,
_timeout: null,
fun: () => {},
start() {
if (this._timeout == null) {
const self = this;
this.fun();
this._timeout = setTimeout(function repeater() {
self.fun();
self._timeout = setTimeout(repeater, 1000 * self.time);
}, 1000 * this.time);
}
},
stop() {
const timeout = this._timeout;
this._timeout = null;
this.set_time(); // set time to default
clearTimeout(timeout);
},
set_time(time) {
if (this._time == null) this._time = this.time;
if (time) {
this.time = time;
} else {
this.time = this._time;
}
},
};
// print time
timer.fun = () =>{
console.log(new Date())
};
timer.set_time(10)
timer.start()
I know this post is old, but i needed something similar, maybe someone needs it.
This is a version without setInterval, based on the code from the other reaction (Niet the Dark Absol).
function timer()
{
var timer = {
running: false,
iv: 5000,
timeout: false,
cb : function(){},
start : function(cb,iv,sd){
var elm = this;
clearInterval(this.timeout);
this.running = true;
if(cb) this.cb = cb;
if(iv) this.iv = iv;
if(sd) elm.execute(elm);
this.timeout = setTimeout(function(){elm.execute(elm)}, this.iv);
},
execute : function(e){
if(!e.running) return false;
e.cb();
e.start();
},
stop : function(){
this.running = false;
},
set_interval : function(iv){
clearInterval(this.timeout);
this.start(false, iv);
}
};
return timer;
}
Usage:
var timer_1 = new timer();
timer_1.start(function(){
//magic here
}, 2000, false);
var timer_2 = new timer();
timer_2.start(function(){
//more magic here
}, 3000, true);
//change the interval
timer_2.set_interval(4000);
//stop the timer
timer_1.stop();
The last parameter of the start function is a boolean if the function needs to be run at 0.
You can also find the script here: https://github.com/Atticweb/smart-interval

javascript: pause setTimeout();

If I have an active timeout running that was set through
var t = setTimeout("dosomething()", 5000)
Is there anyway to pause and resume it?
Is there any way to get the time remaining on the current timeout?
or do I have to in a variable, when the timeout is set, store the current time, then we we pause, get the difference between now and then?
You could wrap window.setTimeout like this, which I think is similar to what you were suggesting in the question:
var Timer = function(callback, delay) {
var timerId, start, remaining = delay;
this.pause = function() {
window.clearTimeout(timerId);
timerId = null;
remaining -= Date.now() - start;
};
this.resume = function() {
if (timerId) {
return;
}
start = Date.now();
timerId = window.setTimeout(callback, remaining);
};
this.resume();
};
var timer = new Timer(function() {
alert("Done!");
}, 1000);
timer.pause();
// Do some stuff...
timer.resume();
Something like this should do the trick.
function Timer(fn, countdown) {
var ident, complete = false;
function _time_diff(date1, date2) {
return date2 ? date2 - date1 : new Date().getTime() - date1;
}
function cancel() {
clearTimeout(ident);
}
function pause() {
clearTimeout(ident);
total_time_run = _time_diff(start_time);
complete = total_time_run >= countdown;
}
function resume() {
ident = complete ? -1 : setTimeout(fn, countdown - total_time_run);
}
var start_time = new Date().getTime();
ident = setTimeout(fn, countdown);
return { cancel: cancel, pause: pause, resume: resume };
}
A slightly modified version of Tim Downs answer. However, since Tim rolled back my edit, I've to answer this myself. My solution makes it possible to use extra arguments as third (3, 4, 5...) parameter and to clear the timer:
function Timer(callback, delay) {
var args = arguments,
self = this,
timer, start;
this.clear = function () {
clearTimeout(timer);
};
this.pause = function () {
this.clear();
delay -= new Date() - start;
};
this.resume = function () {
start = new Date();
timer = setTimeout(function () {
callback.apply(self, Array.prototype.slice.call(args, 2, args.length));
}, delay);
};
this.resume();
}
As Tim mentioned, extra parameters are not available in IE lt 9, however I worked a bit around so that it will work in oldIE's too.
Usage: new Timer(Function, Number, arg1, arg2, arg3...)
function callback(foo, bar) {
console.log(foo); // "foo"
console.log(bar); // "bar"
}
var timer = new Timer(callback, 1000, "foo", "bar");
timer.pause();
document.onclick = timer.resume;
No. You'll need cancel it (clearTimeout), measure the time since you started it and restart it with the new time.
The Timeout was easy enough to find a solution for, but the Interval was a little bit trickier.
I came up with the following two classes to solve this issues:
function PauseableTimeout(func, delay){
this.func = func;
var _now = new Date().getTime();
this.triggerTime = _now + delay;
this.t = window.setTimeout(this.func,delay);
this.paused_timeLeft = 0;
this.getTimeLeft = function(){
var now = new Date();
return this.triggerTime - now;
}
this.pause = function(){
this.paused_timeLeft = this.getTimeLeft();
window.clearTimeout(this.t);
this.t = null;
}
this.resume = function(){
if (this.t == null){
this.t = window.setTimeout(this.func, this.paused_timeLeft);
}
}
this.clearTimeout = function(){ window.clearTimeout(this.t);}
}
function PauseableInterval(func, delay){
this.func = func;
this.delay = delay;
this.triggerSetAt = new Date().getTime();
this.triggerTime = this.triggerSetAt + this.delay;
this.i = window.setInterval(this.func, this.delay);
this.t_restart = null;
this.paused_timeLeft = 0;
this.getTimeLeft = function(){
var now = new Date();
return this.delay - ((now - this.triggerSetAt) % this.delay);
}
this.pause = function(){
this.paused_timeLeft = this.getTimeLeft();
window.clearInterval(this.i);
this.i = null;
}
this.restart = function(sender){
sender.i = window.setInterval(sender.func, sender.delay);
}
this.resume = function(){
if (this.i == null){
this.i = window.setTimeout(this.restart, this.paused_timeLeft, this);
}
}
this.clearInterval = function(){ window.clearInterval(this.i);}
}
These can be implemented as such:
var pt_hey = new PauseableTimeout(function(){
alert("hello");
}, 2000);
window.setTimeout(function(){
pt_hey.pause();
}, 1000);
window.setTimeout("pt_hey.start()", 2000);
This example will set a pauseable Timeout (pt_hey) which is scheduled to alert, "hey" after two seconds. Another Timeout pauses pt_hey after one second. A third Timeout resumes pt_hey after two seconds. pt_hey runs for one second, pauses for one second, then resumes running. pt_hey triggers after three seconds.
Now for the trickier intervals
var pi_hey = new PauseableInterval(function(){
console.log("hello world");
}, 2000);
window.setTimeout("pi_hey.pause()", 5000);
window.setTimeout("pi_hey.resume()", 6000);
This example sets a pauseable Interval (pi_hey) to write "hello world" in the console every two seconds. A timeout pauses pi_hey after five seconds. Another timeout resumes pi_hey after six seconds. So pi_hey will trigger twice, run for one second, pause for one second, run for one second, and then continue triggering every 2 seconds.
OTHER FUNCTIONS
clearTimeout() and clearInterval()
pt_hey.clearTimeout(); and pi_hey.clearInterval(); serve as an easy way to clear the timeouts and intervals.
getTimeLeft()
pt_hey.getTimeLeft(); and pi_hey.getTimeLeft(); will return how many milliseconds till the next trigger is scheduled to occur.
"Pause" and "resume" don't really make much sense in the context of setTimeout, which is a one-off thing. You might want to pause a chained series of setTimeout calls, in which case just don't schedule the next one (perhaps cancel the one that's outstanding via clearTimeout, as below). But setTimeout itself doesn't loop, there's nothing to pause and resume.
If you mean setInterval then no, you can't pause it, you can only cancel it (clearInterval) and then re-schedule it again. Details of all of these in the Timers section of the spec.
// Setting
var t = setInterval(doSomething, 1000);
// Pausing (which is really stopping)
clearInterval(t);
t = 0;
// Resuming (which is really just setting again)
t = setInterval(doSomething, 1000);
/revive
ES6 Version using Class-y syntactic sugar 💋
(slightly-modified: added start())
class Timer {
constructor(callback, delay) {
this.callback = callback
this.remainingTime = delay
this.startTime
this.timerId
}
pause() {
clearTimeout(this.timerId)
this.remainingTime -= new Date() - this.startTime
}
resume() {
this.startTime = new Date()
clearTimeout(this.timerId)
this.timerId = setTimeout(this.callback, this.remainingTime)
}
start() {
this.timerId = setTimeout(this.callback, this.remainingTime)
}
}
// supporting code
const pauseButton = document.getElementById('timer-pause')
const resumeButton = document.getElementById('timer-resume')
const startButton = document.getElementById('timer-start')
const timer = new Timer(() => {
console.log('called');
document.getElementById('change-me').classList.add('wow')
}, 3000)
pauseButton.addEventListener('click', timer.pause.bind(timer))
resumeButton.addEventListener('click', timer.resume.bind(timer))
startButton.addEventListener('click', timer.start.bind(timer))
<!doctype html>
<html>
<head>
<title>Traditional HTML Document. ZZz...</title>
<style type="text/css">
.wow { color: blue; font-family: Tahoma, sans-serif; font-size: 1em; }
</style>
</head>
<body>
<h1>DOM & JavaScript</h1>
<div id="change-me">I'm going to repaint my life, wait and see.</div>
<button id="timer-start">Start!</button>
<button id="timer-pause">Pause!</button>
<button id="timer-resume">Resume!</button>
</body>
</html>
I needed to calculate the elapsed and remaining time to show a progress-bar. It was not easy using the accepted answer. 'setInterval' is better than 'setTimeout' for this task. So, I created this Timer class that you can use in any project.
https://jsfiddle.net/ashraffayad/t0mmv853/
'use strict';
//Constructor
var Timer = function(cb, delay) {
this.cb = cb;
this.delay = delay;
this.elapsed = 0;
this.remaining = this.delay - self.elapsed;
};
console.log(Timer);
Timer.prototype = function() {
var _start = function(x, y) {
var self = this;
if (self.elapsed < self.delay) {
clearInterval(self.interval);
self.interval = setInterval(function() {
self.elapsed += 50;
self.remaining = self.delay - self.elapsed;
console.log('elapsed: ' + self.elapsed,
'remaining: ' + self.remaining,
'delay: ' + self.delay);
if (self.elapsed >= self.delay) {
clearInterval(self.interval);
self.cb();
}
}, 50);
}
},
_pause = function() {
var self = this;
clearInterval(self.interval);
},
_restart = function() {
var self = this;
self.elapsed = 0;
console.log(self);
clearInterval(self.interval);
self.start();
};
//public member definitions
return {
start: _start,
pause: _pause,
restart: _restart
};
}();
// - - - - - - - - how to use this class
var restartBtn = document.getElementById('restart');
var pauseBtn = document.getElementById('pause');
var startBtn = document.getElementById('start');
var timer = new Timer(function() {
console.log('Done!');
}, 2000);
restartBtn.addEventListener('click', function(e) {
timer.restart();
});
pauseBtn.addEventListener('click', function(e) {
timer.pause();
});
startBtn.addEventListener('click', function(e) {
timer.start();
});
Typescript implementation based on top rated answer
/** Represents the `setTimeout` with an ability to perform pause/resume actions */
export class Timer {
private _start: Date;
private _remaining: number;
private _durationTimeoutId?: NodeJS.Timeout;
private _callback: (...args: any[]) => void;
private _done = false;
get done () {
return this._done;
}
constructor(callback: (...args: any[]) => void, ms = 0) {
this._callback = () => {
callback();
this._done = true;
};
this._remaining = ms;
this.resume();
}
/** pauses the timer */
pause(): Timer {
if (this._durationTimeoutId && !this._done) {
this._clearTimeoutRef();
this._remaining -= new Date().getTime() - this._start.getTime();
}
return this;
}
/** resumes the timer */
resume(): Timer {
if (!this._durationTimeoutId && !this._done) {
this._start = new Date;
this._durationTimeoutId = setTimeout(this._callback, this._remaining);
}
return this;
}
/**
* clears the timeout and marks it as done.
*
* After called, the timeout will not resume
*/
clearTimeout() {
this._clearTimeoutRef();
this._done = true;
}
private _clearTimeoutRef() {
if (this._durationTimeoutId) {
clearTimeout(this._durationTimeoutId);
this._durationTimeoutId = undefined;
}
}
}
You could look into clearTimeout()
or pause depending on a global variable that is set when a certain condition is hit. Like a button is pressed.
<button onclick="myBool = true" > pauseTimeout </button>
<script>
var myBool = false;
var t = setTimeout(function() {if (!mybool) {dosomething()}}, 5000);
</script>
You could also implement it with events.
Instead of calculating the time difference, you start and stop listening to a 'tick' event which keeps running in the background:
var Slideshow = {
_create: function(){
this.timer = window.setInterval(function(){
$(window).trigger('timer:tick'); }, 8000);
},
play: function(){
$(window).bind('timer:tick', function(){
// stuff
});
},
pause: function(){
$(window).unbind('timer:tick');
}
};
If you're using jquery anyhow, check out the $.doTimeout plugin. This thing is a huge improvement over setTimeout, including letting you keep track of your time-outs with a single string id that you specify and that doesn't change every time you set it, and implement easy canceling, polling loops & debouncing, and more. One of my most-used jquery plugins.
Unfortunately, it doesn't support pause/resume out of the box. For this, you would need to wrap or extend $.doTimeout, presumably similarly to the accepted answer.
I needed to be able to pause setTimeout() for slideshow-like feature.
Here is my own implementation of a pausable timer. It integrates comments seen on Tim Down's answer, such as better pause (kernel's comment) and a form of prototyping (Umur Gedik's comment.)
function Timer( callback, delay ) {
/** Get access to this object by value **/
var self = this;
/********************* PROPERTIES *********************/
this.delay = delay;
this.callback = callback;
this.starttime;// = ;
this.timerID = null;
/********************* METHODS *********************/
/**
* Pause
*/
this.pause = function() {
/** If the timer has already been paused, return **/
if ( self.timerID == null ) {
console.log( 'Timer has been paused already.' );
return;
}
/** Pause the timer **/
window.clearTimeout( self.timerID );
self.timerID = null; // this is how we keep track of the timer having beem cleared
/** Calculate the new delay for when we'll resume **/
self.delay = self.starttime + self.delay - new Date().getTime();
console.log( 'Paused the timer. Time left:', self.delay );
}
/**
* Resume
*/
this.resume = function() {
self.starttime = new Date().getTime();
self.timerID = window.setTimeout( self.callback, self.delay );
console.log( 'Resuming the timer. Time left:', self.delay );
}
/********************* CONSTRUCTOR METHOD *********************/
/**
* Private constructor
* Not a language construct.
* Mind var to keep the function private and () to execute it right away.
*/
var __construct = function() {
self.starttime = new Date().getTime();
self.timerID = window.setTimeout( self.callback, self.delay )
}(); /* END __construct */
} /* END Timer */
Example:
var timer = new Timer( function(){ console.log( 'hey! this is a timer!' ); }, 10000 );
timer.pause();
To test the code out, use timer.resume() and timer.pause() a few times and check how much time is left. (Make sure your console is open.)
Using this object in place of setTimeout() is as easy as replacing timerID = setTimeout( mycallback, 1000) with timer = new Timer( mycallback, 1000 ). Then timer.pause() and timer.resume() are available to you.
function delay (ms) { return new Promise(resolve => setTimeout(resolve, s)); }
"async" working demo at:
site zarsoft.info
You can do like below to make setTimeout pausable on server side (Node.js)
const PauseableTimeout = function(callback, delay) {
var timerId, start, remaining = delay;
this.pause = function() {
global.clearTimeout(timerId);
remaining -= Date.now() - start;
};
this.resume = function() {
start = Date.now();
global.clearTimeout(timerId);
timerId = global.setTimeout(callback, remaining);
};
this.resume();
};
and you can check it as below
var timer = new PauseableTimeout(function() {
console.log("Done!");
}, 3000);
setTimeout(()=>{
timer.pause();
console.log("setTimeout paused");
},1000);
setTimeout(()=>{
console.log("setTimeout time complete");
},3000)
setTimeout(()=>{
timer.resume();
console.log("setTimeout resume again");
},5000)
class pausable_timeout {
constructor(func, milliseconds) {
this.func = func;
this.date_ms = new Date().valueOf();
this.timeout = setTimeout(func, milliseconds);
this.time_left = milliseconds;
};
pause() {
clearTimeout(this.timeout);
const elapsed_time = new Date().valueOf() - this.date_ms;
this.time_left -= elapsed_time;
};
unpause() {
this.timeout = setTimeout(this.func, this.time_left);
this.date_ms = new Date().valueOf();
};
};
const timer = new pausable_timeout(() => /* your code */, 3000 /* your timeout in milliseconds */);
timer.pause();
timer.unpause();
The programme is rather simple. We will create a class containing two functions, the pause function and the unpause function.
The pause function will clear the setTimeout and store the time that has elapsed between the start and now in the time_left variable. The unpause function will recreate a setTimeout by putting the time_left time as an argument.
If anyone wants the TypeScript version shared by the Honorable #SeanVieira here, you can use this:
public timer(fn: (...args: any[]) => void, countdown: number): { onCancel: () => void, onPause: () => void, onResume: () => void } {
let ident: NodeJS.Timeout | number;
let complete = false;
let totalTimeRun: number;
const onTimeDiff = (date1: number, date2: number) => {
return date2 ? date2 - date1 : new Date().getTime() - date1;
};
const handlers = {
onCancel: () => {
clearTimeout(ident as NodeJS.Timeout);
},
onPause: () => {
clearTimeout(ident as NodeJS.Timeout);
totalTimeRun = onTimeDiff(startTime, null);
complete = totalTimeRun >= countdown;
},
onResume: () => {
ident = complete ? -1 : setTimeout(fn, countdown - totalTimeRun);
}
};
const startTime = new Date().getTime();
ident = setTimeout(fn, countdown);
return handlers;
}
I created this code in TypeScript for slider feature:
class TimeoutSlider {
private callback: () => void;
private duration: number;
private timeReaming: number;
private startTime: number | null = null;
private timerId: NodeJS.Timeout | null = null;
constructor(callback: () => void, duration: number) {
this.callback = callback;
this.duration = duration;
this.timeReaming = duration;
}
public start() {
this.clear();
this.startTime = new Date().getTime();
this.timerId = setTimeout(this.callback, this.duration);
}
public pause() {
if (!this.startTime) {
throw new Error("Cannot pause a timer that has not been started");
}
this.clear();
this.timeReaming = this.duration - (new Date().getTime() - this.startTime);
}
public resume() {
this.clear();
this.startTime = new Date().getTime();
this.timerId = setTimeout(this.callback, this.timeReaming);
}
private clear() {
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
}
}
I don't think you'll find anything better than clearTimeout. Anyway, you can always schedule another timeout later, instead 'resuming' it.
If you have several divs to hide, you could use an setInterval and a number of cycles to do like in:
<div id="div1">1</div><div id="div2">2</div>
<div id="div3">3</div><div id="div4">4</div>
<script>
function hideDiv(elm){
var interval,
unit = 1000,
cycle = 5,
hide = function(){
interval = setInterval(function(){
if(--cycle === 0){
elm.style.display = 'none';
clearInterval(interval);
}
elm.setAttribute('data-cycle', cycle);
elm.innerHTML += '*';
}, unit);
};
elm.onmouseover = function(){
clearInterval(interval);
};
elm.onmouseout = function(){
hide();
};
hide();
}
function hideDivs(ids){
var id;
while(id = ids.pop()){
hideDiv(document.getElementById(id));
}
}
hideDivs(['div1','div2','div3','div4']);
</script>

Categories