Vue.JS countdown not works - javascript

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

Related

JavaScript Scoping Can't Access setInterval

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

Variable scope in setInterval [duplicate]

I have a function:
setInterval(function () {
var counter = 0;
(function() {
counter = counter + 1;
console.log(counter);
})(counter)
}, 1000)
Why does not it increment the counter? (instead, it logs 1's). How to make it log ascending numbers? (1, 2, 3, ....)
You could use a function which returns a function as closure over counter.
setInterval(function(counter) {
return function() {
console.log(++counter);
};
}(0), 1000);
You are passing an argument to your anonymous function, but you aren't assigning that argument to a variable. You forgot to put the arguments in the function definition.
You are creating new variables with every iteration instead of sharing the variable between them. You need to turn your logic inside out.
(function(closed_over_counter) {
setInterval(function() {
closed_over_counter++;
console.log(closed_over_counter);
}, 1000);
})(0);
Since you are using an IIFE instead of a function you can call multiple times, this is pretty pointless. You might as well just declare the variable inside the closure.
(function() {
var counter = 0;
setInterval(function() {
counter++;
console.log(counter);
}, 1000);
})();
Obscured version of Nina Scholz's answer with arrow functions:
setInterval(((counter) => () => console.log(++counter))(0), 1000);

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

Why isn't the variable in this object modified by its callback function?

I'm trying to get a global object to modify one of its own variables in a callback function initialised by one of its own methods. The callback appears to work, but the variable doesn't seem to have been modified when testing the global variable.
Why is the global object not being modified? Are the changes to the global object kept in some sort of staging area pending completion of the callback function?
let obj;
function test_object_flag() {
// 5 - check whether the "timer_finished" flag has been set
console.log("is the timer finished? " + obj.timer_finished); // should return "true"???
}
class TestClass {
constructor() {
this.timer_finished = false;
}
start_timer(ptr_callback_function) {
// 3 - set up a callback for the timer
setTimeout(function() {
// 4 - once the timer is done, set a "timer_finished" flag, and call the callback
this.timer_finished = true;
ptr_callback_function();
}, 1000);
}
}
$( document ).ready(function() {
// 1 - make a new onbject of type TestClass
obj = new TestClass();
// 2 - start the timer
obj.start_timer(test_object_flag);
});
The problem is that setTimeout creates it's own this. Solution may looks like:
start_timer(ptr_callback_function) {
// savig this that your need
const self = this;
setTimeout(function() {
// use needed context
self.timer_finished = true;
ptr_callback_function();
}, 1000);
}
Another option is to use arrow functions:
start_timer(ptr_callback_function) {
setTimeout(() => {
this.timer_finished = true;
ptr_callback_function();
}, 1000);
}

recursive setTimeout - not a variable

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

Categories

Resources