Extend methods in Javascript - javascript

I have two different objects to create a clock. A analogue and digital one. THey're practically the same except for minor changes.
Alot of methods in the object are used by both however; I want them to be instanced though. So i need them in the object. How can I extend for example a Clock object with the basic methods to analogueClock and digitalClock with Javascript?
This is what I have and doesn't work:
the call
if (clockType == 'digital') {
clk = new DigitalClock(theClockDiv);
} else if (clockType == 'analogue') {
clk = new AnalogueClock(theClockDiv);
}
baseClock = new baseClock();
$.extend({}, clk, baseClock);
And the functions
function DigitalClock(theDigitalClockParent, indicatedTime) {
this.indicatedTime = indicatedTime;
this.interval = null;
this.buildClock = function() {
//CUSTOM THINGS HERE
}
this.setCurrentTime();
this.buildClock();
this.startRechecker();
}
function AnalogueClock(theAnalogueClockParent, indicatedTime) {
this.indicatedTime = indicatedTime;
this.interval = null;
this.buildClock = function() {
//CUSTOM THINGS HERE
}
this.setCurrentTime();
this.buildClock();
this.startRechecker();
}
function baseClock() {
this.setCurrentTime = function() {
if (this.indicatedTime != undefined) {
this.date = new Date(railsDateToTimestamp(this.indicatedTime));
} else {
this.date = new Date();
}
this.seconds = this.date.getSeconds();
this.minutes = this.date.getMinutes();
this.hours = this.date.getHours();
}
this.startInterval = function() {
//Use a proxy in the setInterval to keep the scope of the object.
this.interval = setInterval($.proxy(function() {
//console.log(this);
var newTime = updateClockTime(this.hours, this.minutes, this.seconds);
this.hours = newTime[0];
this.minutes = newTime[1];
this.seconds = newTime[2];
this.buildClock();
}, this), 1000);
}
this.stopInterval = function() {
window.clearInterval(this.interval);
this.interval = null;
}
}

You can extend your DigitalClock and AnalogueClock with your base class. Something like following would do.
DigitalClock.prototype = new baseClock();
AnalogueClock.prototype = new baseClock();
So DigitalClock and AnalogueClock will inherit the methods of baseClock. Another option could be to use mixin and extend both classes with it.

Related

I need to remake the function into a class

I have a code for this function. I started learn js classes, and I want to remake this function into class. but I don't understand how to set start and remaining value into constructor correctly. also i don't understand how pass callback function into class
function Timer(callback, delay) {
/* privates properties */
let timerId, start, remaining = delay;
/* Public methods */
this.resume = () => {
start = new Date();
timerId = setTimeout( () => {
remaining = delay;
this.resume();
callback();
}, remaining);
};
this.pause = () => {
clearTimeout(timerId);
remaining -= new Date() - start;
};
this.become = () => {
clearTimeout(timerId);
remaining = delay;
this.resume();
};
/* Constructor */
this.resume();
}
I want to get a class identical to this function
You can do something like this.. Keep in mind, a class in JavaScript is syntactical sugar for a function - under the hood a class is just a function (prototype, really)..
Edit: #georg is right - I shouldn't have used experimental features.. I have updated my answer on how to use class methods and binding them to the class (aka old school way)..
This article explains why you should bind methods to classes.. I have also included an example below (that is commented out) that shows why not binding methods can get you into some trouble.
Edit #2: This answer is incredibly succinct and beautifully explains why binding is important.
class Timer {
constructor(callback, delay) {
this.callback = callback;
this.timerId;
this.start;
this.remaining = delay;
// bind methods to this class
this.resume = this.resume.bind(this);
this.pause = this.pause.bind(this);
this.become = this.become.bind(this);
this.begin = this.begin.bind(this);
}
resume() {
this.start = new Date();
this.timerId = setTimeout(() => {
this.resume();
this.callback();
}, this.remaining);
};
pause() {
clearTimeout(this.timerId);
this.remaining -= new Date() - this.start;
};
become() {
clearTimeout(this.timerId);
this.remaining = this.delay;
this.resume();
};
begin() {
this.resume();
}
}
function timerCallback() {
console.log("Hello from callback!");
}
let timerDelay = 2000; // 2 seconds
let myTimer = new Timer(timerCallback, timerDelay);
myTimer.begin();
/**
*
* This is an example of why you should bind methods to classes.
* If you removed the bindings from the class, this would not work:
*
* let myTimer = new Timer(timerCallback, timerDelay);
* const { begin } = myTimer;
* begin();
*
*/
// This just prevents the timer from running forever.
setTimeout(() => {
myTimer.pause()
}, 10000);

Two instances of same javascript class on one page refer to same instance

I am attempting to write a Countdown timer script, and it works with only one instance on the page, but if I add a second one, only the second starts counting.
I discovered that if I load both, but only call the start for the second one, the first one fires. It appears that they are not scoped correctly.
I'm using the new class syntax, so I thought it should work as is, but I'm obviously missing something. I'm hoping someone can help me understand what I'm doing wrong. My primary language is PHP, and I am not as well versed in JS as I'd like to be.
Here is a link to my gist which contains the code: https://gist.github.com/kennyray/b35f4c6640be9539c5d16581de7714e0
class CountdownTimer {
constructor(minutesLabel = null, secondsLabel = null) {
self = this;
this.minutesLabel = minutesLabel;
this.secondsLabel = secondsLabel;
this.totalSeconds = (this.minutesLabel.textContent / 60) + this.secondsLabel.textContent;
this.timer = null;
}
set minutesLabel(value) {
self._minutesLabel = value;
}
set secondsLabel(value) {
self._secondsLabel = value;
}
get minutesLabel() {
return self._minutesLabel;
}
get secondsLabel() {
return self._secondsLabel;
}
setTime() {
self.totalSeconds--;
if (parseInt(self.minutesLabel.innerHTML) == 0 && parseInt(self.secondsLabel.innerHTML) == 0) { self.stopTimer; return;}
if (self.secondsLabel.innerHTML.textContent < 0) { self.secondsLabel.innerHTML = 59 }
if (self.minutesLabel.innerHTML.textContent < 0) { self.minutesLabel.innerHTML = 59 }
self.secondsLabel.innerHTML = self.pad((self.totalSeconds % 60));
self.minutesLabel.innerHTML = self.pad(Math.floor(self.totalSeconds / 60));
}
pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
resetTimer() {
clearInterval(self.timer);
self.totalSeconds = 0;
self.secondsLabel.innerHTML = self.pad(self.totalSeconds % 60);
self.minutesLabel.innerHTML = self.pad(parseInt(self.totalSeconds / 60));
}
startTimer() {
self.timer = setInterval(self.setTime, 1000);
}
stopTimer() {
clearInterval(self.timer);
}
}
const t1 = new CountdownTimer(document.getElementById("minutes1"), document.getElementById("seconds1"));
t1.startTimer();
const t2 = new CountdownTimer(document.getElementById("minutes"), document.getElementById("seconds"));
console.log(t1.startTimer() === t2.startTimer());
t2.startTimer();
<label id="minutes1">01</label>:<label id="seconds1">10</label>
<br>
<label id="minutes">00</label>:<label id="seconds">10</label>
You're declaring a global variable self (why the hell do you do this?) that get's overiden. Just use this in a class.
Your startTimer function then needs to be
startTimer() {
this.timer = setInterval(this.setTime.bind(this), 1000);
}
and should maybe check if there's already an interval and clear this.timer completely
startTimer() {
if (this.timer) this.stopTimer();
this.timer = setInterval(this.setTime.bind(this), 1000);
}
stopTimer() {
clearInterval(this.timer);
this.timer = null;
}
It really boils down to this line
self = this;
By not including the keyword var you elevate that to global scope. If you want to use self instead of this in the ctor (which is perfectly fine) just prefix it with var:
var self = this;

Javascript / Jquery OOP not inheriting attributes

I have created a Constructor class / function that has 4 methods and a 5 attributes. The problem is when i create a new instance of the constructor it doesn't inherit the first attribute (this.el = element) which is 'affiliateSection' on the new instance called: animation1. Thanks in advance..
// main class
function AnimateImages(el, time, effect, setTime, h){
this.el = element; // this is not inheriting from 'animate1'
this.time = time;
this.effect = effect;
this.setTime = setTime;
this.h = height;
this.delayAnimate = function() {
this.element.delay(time)
.queue(function(next) {
$(this).addClass(effect);
next();
});
};
// function for multi animations
var multiAnimations = function() {
var i = 0;
this.element.each(function (key, value) {
i = i + setTime;
var tthis = this;
delayAnimate($(this), i, tthis.effect);
});
};
// load on window height
var onWindowAnimate = function () {
if ($(this).scrollTop() > this.height) {
// call multi animations function
var tthis = this;
multiAnimations(this.element, tthis.setTime, tthis.effect);
}
};
// hide function
var hideAnimatedEl = function (){
this.element.each(function(){
$(this).css("visibility", "hidden");
});
};
} // End of AnimateImages
/*============================================================*/
var affiliateSection = $("#aff-img > li > img");
// new instance of AnimateImages
var animation1 = new AnimateImages(affiliateSection, 200, 'subtlefadeIn',
300, 50);
$(window).scroll(function () {
setTimeout(function(){
animation1.onWindowAnimate();
}, 1000);
});
It looks like you have your member variable initializations backwards. Try this:
this.element = el; // this is not inheriting from 'animate1'
this.time = time;
this.effect = effect;
this.setTime = setTime;
this.height = h;
Your parameter name is wrong:
this.el = element;
element is not in the parameter list.
Since you are reffering to this.element inside your function, I am assuming that your first line should be
this.element = el;

JavaScript field logged to console is always "undefined"

It's the first time i use objects with JavaScript, i used the method 1.1 from this tutorial, i have this code:
function MyClass() {
this.currentTime = 0;
this.start = function() {
this.currentTime = new Date().getTime();
console.log(this.currentTime); //this line prints the time i just set
this.intervalID = setInterval(this.step, 25);
};
this.step = function() {
var d = new Date().getTime();
console.log(this.currentTime); //always prints "undefined" to the console
};
this.stop = function() {
clearInterval(this.intervalID);
};
}
The problem is that in the step() function, console.log(this.currentTime)always prints "undefined", while this.currentTime was set in the start() function.
Why? What am i missing?
You are using the scope of the function this.fn in each case, that's why you're not adding it to the MyClass's scope. You have to store the this object and use it to add properties.
function MyClass() {
this.currentTime = 0;
var self = this;
this.start = function() {
self.currentTime = new Date().getTime();
console.log(self.currentTime); //this line prints the time i just set
self.intervalID = setInterval(self.step, 25);
};
this.step = function() {
var d = new Date().getTime();
console.log(self.currentTime); //always prints "undefined" to the console
};
this.stop = function() {
clearInterval(self.intervalID);
};
}

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