Calling class methods inside javascript class - javascript

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

Related

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

How to stop a function in another function

How i can stop a function in another function?
For example:
var snow = function(){
var interval = setInterval( function(){
alert('letItSnow')
}, 1000);
};
snow();
clearInterval(snow.interval) - exception
In javascript, access scopes are limited via function declarations, so your locally declared variables won't be accessible outside, hence you must return it or set it to a global variable (variable available in parent scope)
you need to make a slight adjustment to your function, do it like this:
var snow = function(){
return setInterval(function(){
alert('letItSnow');
}, 1000);
};
var interval = snow();
//on some event -- clearInterval(interval)
you can also make the setTimeout and its returned id a property to the function which would be available on all of its instances i.e.
var snowClass = function(){
this.init = function(msg){
this.interval = setInterval(function(){alert(msg)},1000);
}
}
var snowObj = new snowClass();
snowObj.init('Let it snow');
//on some event -- clearInterval(snowObj.interval)
you referring to snow.interval which assumed to be property of snow object. but in your code interval is just local variable. instead you might want to define interval in the global scope so it will be accessible globally http://www.w3schools.com/js/js_scope.asp
var interval, snow = function(){
interval = setInterval( function(){
console.log('letItSnow')
}, 1000);
};
snow();
clearInterval(interval);
If I understand the question correctly, you want to stop the interval outside of the snow function.
You can declare the interval variable outside of the snow function in order to use it (to clear the interval) outside of the snow function.
var interval;
var snow = function(){
interval = setInterval(
function(){
alert('letItSnow')
},
1000
);
};
snow();
clearInterval(interval);
try this in your code
var timeout1 = {};
var timeout2 = {};
function function1(){
//codes
if(timeout2){
clearTimeout(timeout2);
}
timeout1 = setTimeout("function1()",5000);
}
function function2(){
//codes
if(timeout1){
clearTimeout(timeout1);
}
timeout2 = setTimeout("function2()",5000);
}

JavaScript: setTimeout (setInterval) not working

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

Add a timeout to "setInterval()"?

I have a setInterval function like below on a Divbox so if I leave a divbox, this setInterval is triggered:
setInterval("playthis()", 1000);
What I want it to do: If I leave the divbox and lets say within the next 2 second rehover it, the setInterval should not triggered.
Is this possible?
You can use cousins setTimeout and clearTimeout to set a function callback that invokes your setInterval only after 2 uninterrupted seconds:
var handle = null;
function yourDivboxLeaveHandler() {
handle = setTimeout(startPlay, 2000);
}
function yourDivboxHoverHandler() {
if (handle !== null) {
clearTimeout(handle);
handle = null;
}
}
function startPlay() {
setInterval(playthis, 1000); // use function references please, not eval
handle = null;
}
You will want much better variable/function names than this though.
Yes. Just make some creative use of clearInterval().
In other words, no, such a feature doesn't come out-of-the-box, but you can build it yourself by calling clearInterval() if the mouse re-enters the divbox before the interval is triggered.
For example:
var divBox = document.getElementById('MyDivBox');
var TimeoutHandle = null;
divBox.onmouseover = function()
{
if ( TimeoutHandle != null )
{
clearTimeout(TimeoutHandle);
}
}
divBox.onmouseout = function()
{
TimeoutHandle = setTimeout(function()
{
TimeoutHandle = null;
setInterval(playthis, 1000);
}, 2000);
}
First of all is a bad practice to have the code evalued in a setInterval so you should avid double quotes. Then you can clear the interval like this:
var int = setInterval(playthis, 1000);
clearInterval(int)

Javascript Timeout Issue

I have this Javascript class:
function PageManager () {
this.timeoutHandler = function () {
alert ("hello");
}
this.startTimeout = function () {
this.timeout = setTimeout ("this.timeoutHandler()", 1000);
}
}
When I call obj.startTimeout (); I get this error:
this.timeoutHandler is not a function
How do I call a class function in the timeout?
If you pass a string to setTimeout, the code is evaluated in the global scope. Always pass a function reference:
this.startTimeout = function () {
var self = this;
this.timeout = setTimeout(function() {
self.timeoutHandler();
}, 1000);
}
Or if you don't need a reference to the object inside timeoutHandler, then you can pass the function directly:
this.timeout = setTimeout(this.timeoutHandler, 1000);
The problem is that you're passing setTimeout a string. This string is eval'd, with a scope of the window. So if you were to do this:
this.timeout = setTimeout ("console.log(this);", 1000);
... with Firebug installed, you'd see that this is window, which does not have a timeoutHandler method, of course.
This is why you should never, ever pass setTimeout a string. Give it a function reference instead.
function PageManager () {
this.timeoutHandler = function () {
alert ("hello");
console.log(this);
}
this.startTimeout = function () {
this.timeout = setTimeout (this.timeoutHandler, 1000);
}
}
obj = new PageManager ();
obj.startTimeout();
When you execute this code, you'll have the scope you're expecing.

Categories

Resources