I'm building an JS application where I'm using multiple timers (digital, analog). I would like to use a base class for the Timer with the functions: start, stop, update, etc.
Every time there is a timer created there are also new onChange event created. So when the timer ticks multiple instances get an update, not only the one where the timer is created in.
My question is: how can I bind and Timer instance the another class?
Timer class:
class Timer = {
constructor() {
this.seconds = 0;
}
start() {
this.timer = setInterval(update, 25);
}
stop() {
clearInterval(this.timer);
}
update() {
this.seconds += 1;
//emit data
let event = new Event("timer-tick");
event.detail = {
seconds: seconds,
}
document.body.dispatchEvent(event);
}
}
DigitalTimer class:
class DigitalTimer = {
constructor() {
this.timer = new Timer();
this.handleEvent();
}
handleEvent() {
$('body').on('timer-tick', function(e) {
//tick, do somehting with it.
});
}
start() {
this.timer.start();
}
stop() {
this.timer.stop()
}
}
There is a bind method on the Function prototype that does what you want.
start() {
this.timer = setInterval(this.update.bind(this), 25);
}
On a side note, you shouldn't rely on setInterval or setTimeout to increment the time. Of course they are useful to make periodic calls, but the elapsed time isn't guaranteed. You can instead compare an initial Date object with a new one on each call.
I did get it working by binding an on and trigger event on a plain object.
http://api.jquery.com/jQuery/#working-with-plain-objects
Working sample:
https://jsfiddle.net/q5s6cud3/
class Timer {
constructor() {
let self = this;
this.timer = setInterval(function() {
self.update();
}, 1000);
}
update() {
$(this).trigger('timer-tick');
}
}
class DigitalTimer {
constructor() {
this.timer = new Timer();
$(this.timer).on('timer-tick', function() {
console.log('yes');
});
}
}
const digitalTImer = new DigitalTimer();
Related
Hey guys can someone just quickly help me out here.
I have an interval for a slideshow in one function and I want to clear it from another function without using global scopes as I know it is bad practice.
Can someone kindly help here please?
function beginSlideshow() {
var interval = setInterval(function () {
//Slideshow content here
}
function revertSlideshow() {
clearInterval(interval);
}
You have to store the timer handle somewhere. :-)
You have lots of options:
Modules
You could use modules. Then a top-level declaration of interval wouldn't be a global, it would only be accessible to the module:
let interval = 0;
export function beginSlideshow() {
interval = setInterval(function () {
//Slideshow content here
}, someValue);
}
export function revertSlideshow() {
clearInterval(interval);
interval = 0;
}
In a closure's scope
Similar concept to the module above, but without using modules:
const { beginSlideshow, revertSlideshow } = (() => {
let interval = 0;
function beginSlideshow() {
interval = setInterval(function () {
//Slideshow content here
}, someValue);
}
function revertSlideshow() {
clearInterval(interval);
interval = 0;
}
return { beginSlideshow, revertSlideshow };
})());
In the caller's scope
You could make this the problem of the person calling beginSlideshow by returning the function to stop it:
function beginSlideshow() {
const interval = setInterval(function () {
//Slideshow content here
}, someValue);
return () => {
clearInterval(interval);
};
}
The caller would use that like this:
const revertSlideshow = beginSlideShow();
// ...
revertSlideshow();
Another way to store it in the caller's scope is to wrap this up in a class and have the handle be a data property:
class Slideshow {
interval = 0;
begin() {
this.interval = setInterval(/*...*/);
}
revert() { // I'd call it "end"
clearInterval(this.interval);
this.interval = 0;
}
}
I am new to JavaScript and coming not forth with my current issue.
I have here an React App and I try to expand it by a timer functionality. I created a timer (similar to this question: setInterval in a React app) and the timer functionality itself is working.
But the timer callback (or more exactly the setInterval callback) is not working. In the setInterval the variable this is pointing to Windows but I thought it should point to App, which is the class/object who created the timer and contains the setInterval callback. Therefore I get following error.
Here are the crucial code changes I made:
class App extends Component {
//...
componentDidMount() {
//...
var intervalId = setInterval(this.timer, 5000);
// store intervalId in the state so it can be accessed later:
this.setState({intervalId: intervalId});
}
componentWillUnmount() {
//...
// use intervalId from the state to clear the interval
clearInterval(this.state.intervalId);
}
timer() {
for (let i = 0; i < this.state.whitelist.length; i++) {
this.requestDeviceStatus(this.state.whitelist[i]);
}
}
//...
}
But you can access the complete source code/commit here:
https://github.com/wewa00/Reacticz/blob/backgroundtimer/src/App.js,
(And this are the Relevant commits: 02e3e93, 6bcabba.)
Why this pointing to Window and how can I access App from within timer()?
TypeError
Use timer function with ES6 arrow method.
timer=()=>{
for (let i = 0; i < this.state.whitelist.length; i++) {
this.requestDeviceStatus(this.state.whitelist[i]);
}
}
You need to use bind to access App inside timer function.
class App extends Component {
//...
componentDidMount() {
//...
var intervalId = setInterval(this.timer, 5000);
// store intervalId in the state so it can be accessed later:
this.setState({intervalId: intervalId});
this.timer = this.timer.bind(this); // <------------------------------
}
componentWillUnmount() {
//...
// use intervalId from the state to clear the interval
clearInterval(this.state.intervalId);
}
timer() {
for (let i = 0; i < this.state.whitelist.length; i++) {
this.requestDeviceStatus(this.state.whitelist[i]);
}
}
//...
}
This is a Vue class. The method signOut() should fire when the timer ticks. The timer works, except the call signOut().
The problem is with accessing the class method. I'm confused with this, self and access modifiers.
I tried with this.signOut() but it does not work.
How can I call the method signOut?
"use strict";
(async (globals, config, loader, application) => {
const storageLocal = await loader.services.storage.local.getAsync();
class HeaderComponent {
#foo = a;
constructor(tag) {
this.tag = tag;
this.timer();
}
signOut() {
storageLocal.delete('account');
window.location = '/signin.html';
}
timer() {
//document.getElementById("timer"),
var counter = -1;
var timeout;
var startTimer = function timer() {
counter++;
console.log(counter);
signOut(); //<- error can't call class method
timeout = setTimeout(timer, 10000);
};
function resetTimer() {
// here you reset the timer...
clearTimeout(timeout);
counter = -1;
startTimer();
//... and also you could start again some other action
}
document.addEventListener("mousemove", resetTimer);
document.addEventListener("keypress", resetTimer);
startTimer();
}
data() {
return { account: storageLocal.account };
}
}
const component = new HeaderComponent('component-header')
loader.components.set(component.tag, component);
})(window, window.config, window.loader, window.application);
Please note:
signOut() {
storageLocal.delete('account');
window.location = '/signin.html';
}
timer() {
//document.getElementById("timer"),
var counter = -1;
var timeout;
var startTimer = function timer() {
as you can see 'signOut()' is 2 levels below active functions. The logic says it would work like this.parent.signOut() but it DOES NOT !
EDIT3: this.signOut(); will produce
Uncaught TypeError: Cannot read property 'signOut' of undefined
at timer (header.js:30)
at HTMLDocument.resetTimer
The function creates a new context. You need to switch to arrow function and use this.signOut(). Simplified example:
timer() {
var counter = -1;
var timeout;
var startTimer = () => {
counter++;
console.log(counter);
this.signOut();
timeout = setTimeout(startTimer, 1000);
};
setTimeout(startTimer, 1000);
}
Moreover, you have two signOut() methods defined in one class.
You need this and call it like this.signOut()
The startTimer-function does not run in the context of the HeaderComponent's instance.
this in startTimer will point to window when it's executed as a handler in setTimeout.
In order to access the the instance of HeaderComponent, either use an arrow function (as pointed out in an earlier answer. See also Arrow function expressions) which will point this to the outer context (which is HeaderComponent's instance) or define an identifier in timer which points to the instance (eg. const self = this;) and use self instead of this in startTimer.
To apply this to your example (for the sake of consistency, I used var instead of const):
timer() {
var counter = -1;
var timeout;
var self = this;
var startTimer = function() { // Don't use a named function here, it only leads to more confusion
counter++;
console.log(counter);
self.signOut(); // Use `this` of the outer context
timeout = setTimeout(startTimer, 10000); // Use the declared identifier
};
// Rest of the method
}
this is Javascript may be a bit confusing to those who come from different programming languages. If you want to get into more detail, I recommend reading into the MDN reference for this and into Closures
I want to stop setInterval from another function defines in the same class :
class A
{
blink(){};
stopBlink(){};
}
class B extends A
{
constructor(oldMaterial)
{
super();
this.oldMaterial=oldMaterial;
this.interval=null;
this.blink=this.blink.bind(this);
this.stopBlink=this.stopBlink.bind(this);
}
blink(obj,delay,box,duration)
{
var changeMaterial= function(obj,newMaterial)
{
{
obj.material=newMaterial;
}
}
this.interval=(function()
{
var Toggle=true
return setInterval(function()
{
if(Toggle)
changeMaterial(obj,box);
else
{ changeMaterial(obj,OriginalStairMaterial);
}
Toggle=!Toggle;
}
, delay);
})();
}
stopBlink(obj,duration)
{
setTimeout(function()
{
obj.material=oldMaterial;
clearInterval(this.interval);
},duration);
}
}
I bind() this in order to be accessible in the stopBlink() function, but setInterval() doesn't stop when stopBlink() is called. I don't want to use Interval as a global variable :(
Best Regards
Anes
Ok, so basically i'm creating an Interval class to handle repeating actions.
I have something like this:
function Interval(fn, speed) {
this.fn = fn;
this.speed = speed;
this.nt = setInterval(fn, speed);
}
And then i have 3 methods:
this.pause = function() {
clearInterval(this.nt);
}
this.start = function() {
this.nt = setInterval(this.fn, this.speed);
return this.nt;
}
this.wait = function(time) {
this.pause();
setTimeout(function() {
this.start();
}, time);
}
The problem appears in the third method. this.pause(); and this.start(); works as expected. But when I nest this.start into a setTimeout function it stops working. I don't understand why. Here's an example:
var i = 0:
var nt = new Interval(function() {
alert('Itineration: '+ i );
if(i>5);
nt.pause();
setTimeout(nt.start, 2000);
// nt.wait(2000);
}, 500);
Neither nt.wait(2000); nor nt.pause(); setTimeout(nt.start, 2000); is working.
this inside the timeout handler is not the Interval object, it is referring to the window object(not strict mode) so this.start() will not work
One solution is to pass a custom context using Function.bind()
this.wait = function (time) {
this.pause();
setTimeout(function () {
this.start();
}.bind(this), time);
// setTimeout(this.start.bind(this), time) as #elclanrs suggested
}
You are running into a context issue with your code. When the setTimeout function executes your callback the definition of "this" is no longer your Interval object. You need to modify your code so that you maintain a proper reference to the Interval object.
this.wait = function(time) {
var interval = this;
interval.pause();
setTimeout(function() {
interval.start();
}, time);
}
Edit
I just saw the other answer using .bind which is a much cleaner solution from a readability standpoint. One important note about .bind is that behind the scenes it basically generates another function to call your original function using the .call or .apply methods to set the correct value of this
In most cases the readability gained from using .bind is worth it. However, if this is going to be a core component to a larger system, it is a good idea to squeeze every ounce of performance you can out of it. Which would be an argument for avoiding .bind in this specific situation.
Working example based on the other answers.
function Interval(fn, speed) {
this.fn = fn;
this.speed = speed;
this.nt = setInterval(fn, speed);
this.pause = function () {
clearInterval(this.nt);
}
this.start = function () {
this.nt = setInterval(this.fn, this.speed);
return this.nt;
}
this.wait = function (time) {
this.pause();
setTimeout(function () {
this.start();
}.bind(this), time);
}
}
var i = 0;
var nt = new Interval(function () {
document.write('<pre>Itineration: ' + i + '</pre>');
i++;
nt.wait(2000);
}, 500);