JavaScript Scoping Can't Access setInterval - javascript

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;
}
}

Related

Calling class methods inside javascript class

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

Bind class to an instance of another class

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();

Vue.JS countdown not works

I have a vue application, but the countdown not work good.
Actually i dont know why.
View {{ $parent.timer }} i see the good value.
Vue data:
data: function() {
return {
timer : 3,
...
and here is my countdown function:
countdown : function(time,callback)
{
//time is equal 5
this.timer = time;
//this.timer is equal 5
var timerInterval = undefined;
timerInterval = setInterval(function() {
if(this.timer == 1) {
this.timer = 0;
callback();
return clearInterval(timerInterval);
}
// First time undefined, in 2nd is NaN
this.timer -= 1;
// NaN
}, 1000);
}
call function:
this.countdown(data.time,function(){ //smtng });
What i do bad? Its work in my older Vue application.
I hope someone can help to me :)
Thanks so much!
It is an issue with scope of this, as explained below:
function() {...} creates a new scope inside. If you use this inside this function, it does not refer to outer scope. Therefore your this.timer of Vue component does not get updated from inside your setInterval().
() => {...} works like a function but does not create a new scope inside.
Check if the following code works:
timerInterval = setInterval(() => {
if(this.timer == 1) {
this.timer = 0; // `this` points to the scope of Vue component
callback();
// ...
}
// ...
}, 1000);
More info on arrow functions: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

JS how to get variable that invoked function?

i would like to get the variable that invoke a function, i'v searched but i didn't find..
To be clearer,
var time = 60
function timer() {
variableName--
console.log(variableName)
return variableName
}
setInterval(() => {
time = timer()
}, 1000)
So, i would like to know variableName, in fact variableName is time so i can do
time = timer(time)
But, it's not very.. i don't know, there is obviously a way to get variable assigned to a function, (i don't know how to say that but i hope you understood me)
So, i want my code to be the more clearest and reusable as posible,
In fact, i'm programing a socket timer,
It's like:
socket.on('timerStart', () => {
TIMER = timer()
console.log(TIMER)
}
function timer() {
if(variableThatInvoked > 0) {
variableThatInvoked--
return variableThatInvoked
}
}
So, how to get variableThatInvoked ?
You could try something like this:
var time = function timer() {
console.log("time called");
}
Below is your async function which uses setTimeout
function async(your_function, callback) {
setTimeout(function() {
your_function();
if (callback) {callback();}
}, 0);
}
async(time, function() {console.log(time);});
It's perfectly fine to do something like
var time = 60
function timer(variableName) {
variableName--
console.log(variableName)
return variableName
}
setInterval(() => {
time = timer(time)
}, 1000)
You can exactly see what you are passing.
And in time = timer() for the function call there's no reference to time at any point
Well, thank you guys, i'v seen that it's not posible, so i'v turned it into class logic
So, for thoses who were like me, there is the Timer class:
class Timer {
constructor(left, rate, func, callback) {
this.left = left
this.rate = rate
this.func = func
this.cb = callback
}
start() {
this.interval = setInterval(() => {
this.func()
this.left--
if(this.left < 0) {clearInterval(this.interval); this.cb()}
}, 1000 / this.rate)
}
}
And so, when a receive a server socket who say 'hey, let's begin the timer !':
socket.on('startTimer', (servLeft) => {
timer = new Timer(servLeft, 2, () => {
console.log(timer.left)
}, () => {console.log('timer ended!')})
timer.start()
})
Logical rendering:
<!DOCTYPE html>
<html>
<head>
<title>Timer</title>
<script>
class Timer {
constructor(left, rate, func, callback) {
this.left = left
this.rate = rate
this.func = func
this.cb = callback
}
start() {
this.interval = setInterval(() => {
this.func()
this.left--
if(this.left < 0) {clearInterval(this.interval); this.cb()}
}, 1000 / this.rate)
}
}
timer = new Timer(20, 1, () => {console.log(timer.left)}, () => {console.log('Timer ended!')})
timer.start()
</script>
</head>
<body>
<hi>Simple timer</hi>
</body>
</html>

how to execute a function that inside it is another function and pass params

I have a function like this :
$.SetInLocalStorageVideoTime = function (uuid) {
alert(uuid);
var Interval = setInterval(function () {
localStorage.setItem('poption-ctime-'+ uuid , jwplayer("target").getPosition());
},10000);
var ClearInterVal = clearInterval(Interval);
return {
Interval : Interval,
ClearInterVal : ClearInterVal
}
};
My problem is how to call the Interval function and pass uuid param to that.
I have tried $.SetInLocalStorageVideoTime("blahblah").Interval(); but it throws an error.
var Interval = setInterval(...)
This immediately calls the setInterval function and assigns its return value to Interval; same for clearInterval. You don't want to call the function, you want to create a function which when called calls the function. Two ways to do that:
var Interval = function () {
setInterval(...);
}
var Interval = setInterval.bind(null, ...);
Putting it all together, you want this:
$.SetInLocalStorageVideoTime = function (uuid) {
var interval = null;
var set = function () {
interval = setInterval(function () {
localStorage.setItem('poption-ctime-'+ uuid , jwplayer("target").getPosition());
}, 10000);
};
var clear = function () {
clearInterval(interval);
};
return {
Interval : set,
ClearInterVal : clear
}
};
Look this plunker : https://plnkr.co/edit/7H61Vv6m8M552CNeIpSA?p=preview
You must encapsulate into function :
var stop;
var interval = function () {
stop = setInterval(function () {
console.log(uuid);
},100);
}
var ClearInterVal = function () { clearInterval(stop) };
You have several simple issues, you must export function that will clearTimeout
$.SetInLocalStorageVideoTime = function(uuid) {
// auto start interval, you could
// add starter function or something
var Interval = setInterval(function() {
localStorage.setItem('poption-ctime-' + uuid, jwplayer("target").getPosition());
}, 10000);
// clear function
// exported
var ClearInterVal = function() {
if (Interval)
clearInterval(Interval);
}
return {
// Interval is not required here
ClearInterVal: ClearInterVal
}
};
$.SetInLocalStorageVideoTime();

Categories

Resources