I can't change global variable inside setInterval es6 - javascript

I have to find a button when it will appear. In order to do that I use setInterval. When it finds this button, it gives to my variable needed value. I check it inside the setTimeout, but after setTimeout(outside these method) my global variable became as before setTimeout. How to fix that?
let foundValue;
function findById(id) {
let interval = setInterval(() => {
if (document.getElementById(id)){
let foundValue = document.getElementById(id);
clearInterval(interval);
}
}, 1000);
return foundValue;
}

It's because you're re-declaring foundValue inside setInterval so you should remove the second let, for example:
let foundValue;
function findById(id) {
let interval = setInterval(() => {
if (document.getElementById(id)){
foundValue = document.getElementById(id);
clearInterval(interval);
}
}, 1000);
return foundValue;
}

Related

SetTimeout recursive with arrow function

I would like to know if there is a way to use setTimeout recursive implements with an arrow function, in order to use this (refers to my class attribute for example) inside. Indeed, this = undefined when i declare my setTimeout with a normal function
I got :
public currentIndex: number = 0;
setTimeout(function run(){
this.currentIndex++;
console.log(this.currentIndex); // returns undefined
setTimeout(run, 1000);
}, 1000)
Instead of :
setTimeout(() => {
this.currentIndex++;
console.log(this.currentIndex) // returns currentIndex value
setTimeout( ?? , 1000) // What should i put instead of '??' ?
}, 1000)
You could bind this first and then use this function for all calls.
function run(reference) {
this.currentIndex++;
console.log(this.currentIndex); // returns undefined
setTimeout(reference, 1000, reference);
}
const runThis = run.bind(thisReference);
setTimeout(runThis, 1000, runThis);
Its because arrow function does not create new context inside arrow function body but normal function does. So this in arrow function refers to parent scope context but this in normal function refers to its own context.
This will create setTimeouts recursively
let currentIndex = 0;
const run = () => {
setTimeout(() => {
currentIndex++;
console.log(currentIndex);
run();
}, 1000);
}
run();
but better approach may be (I don't know your use case, so it is just maybe) to use setInterval()
let currentIndex = 0;
const interval = setInterval(() => {
currentIndex++;
console.log(currentIndex);
// stop interval
if (currentIndex >= 10) {
clearInterval(interval);
}
}, 1000);
Probably the easiest way is to extract the arrow function into its own variable:
const run = () => {
this.currentIndex++;
console.log(this.currentIndex);
setTimeout(run, 1000);
};
setTimeout(run, 1000);
Though in this particular example you could simplify it even more using setInterval instead of setTimeout, avoiding the second setTimeout call entirely.

I need to call ClearInterval from a different function

I'm creating a countdown timer and I need to call clearInterval from a different function as I want to start and pause the countdown with two different buttons
Here's a piece of my code
const startCountdown = () => {
const countdown = setInterval(() => {
setSecondsElapsed(secondsElapsed => secondsElapsed - 1);
}, 1000);
};
const pauseCountdown = () => {
clearInterval(countdown);
};
The countdown starts when I press the initial button but it doesn't pause when I press the button calling pauseCountdown()
Use a React ref to hold the timer reference. When setting the interval store the countdown reference as the ref's current value, and in the other function clear the interval using the ref's current value.
const countDownRef = React.useRef();
const startCountdown = () => {
countDownRef.current = setInterval(() => {
setSecondsElapsed(secondsElapsed => secondsElapsed - 1);
}, 1000);
};
const pauseCountdown = () => {
clearInterval(countDownRef.current);
};
try declaring countdown globally so it can be accessed from any function. I'd also recommend using var instead of const for things that will be frequently redefined, such as a pausable countdown loop.
try this:
var countdown;
const startCountdown = () => {
countdown = setInterval(() => {
setSecondsElapsed(secondsElapsed => secondsElapsed - 1);
}, 1000);
};
const pauseCountdown = () => {
clearInterval(countdown);
};
The countdown value is inside the scope of startCountdown function hence it can not be accessed from pauseCountdown function which is not inside that very scope.
There are many different ways to do this job properly. I would advise you to be more structural with the help of new abstractions.
I may perhaps use a Countdown class for this job.
class Countdown {
#sid;
#start;
constructor(start){
this.#start = start;
}
startCountdown(){
this.#sid = setInterval( _ => ( !this.#start && clearInterval(this.#sid)
, console.log(this.#start--)
)
, 1000
);
}
pauseCountdown(){
clearInterval(this.#sid);
}
}
var cd = new Countdown(10);
cd.startCountdown();
setTimeout(_ => cd.pauseCountdown(), 5001)
setTimeout(_ => cd.startCountdown(), 10000)
The private class fields #sid and #start keep the clearInterval id and starting value respectively. The thing is they are not exposed to the outer world.

debounce function not working in javascript

I'm having problem understanding why the below debounce code does not work?
you can see the below code in the following: link
`
HTML:
<input type="text" onkeyup="betterFunction(event)"/>
JS:
let newValue;
let counter = 0;
const getData = () => {
// dummy call to API and get Data
console.log("Fetching Data ..", newValue,counter++);
}
const debounce = function (fn, d) {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, d);
}
}
const betterFunction = ({target:{value}}) => {
newValue=value;
debounce(getData, 2000); // **line 1**. placing this line of code debouncing does not happen
intermediate() // **line 2**. replacing this line of code with the above line debouncing works
}
const intermediate = debounce(getData, 2000);
`
I understand that the debounce function returns another function which acts like a closure in JavaScript but why the above line 1 code does not work but the line 2 code works
debounce function returns a function which is never called when you call debounce as
debounce(getData, 2000);
dobounce function doesn't needs to return a function. You just need following steps to implement debounce function:
Check if timer is undefined or not. If not, that means there's a timeout that we need to cancel.
After that set a new timer by calling setTimeout() that calls the given function after specific amount of time.
Also, timer should not be a local variable because you don't want it to reset whenever debounce function is called.
let counter = 0;
let newValue;
let timer;
const getData = () => {
console.log("Fetching Data ..", newValue, counter++);
}
const debounce = function(fn, d) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, d);
}
const betterFunction = (e) => {
newValue = e.target.value;
debounce(getData, 2000);
}
<input type="text" onkeyup="betterFunction(event)" />
If you don't want to declare timer as a global variable and want to return a function from debounce function, then you need to call the debounce function once initially and whenever keyup event fires on the input element, you call the function returned from the debounce function instead of calling the debounce function.
let counter = 0;
let newValue;
const getData = () => {
console.log('Fetching Data ..', newValue, counter++);
};
const debounce = function(fn, d) {
let timer;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, d);
};
};
const intermediate = debounce(getData, 2000);
const betterFunction = (e) => {
newValue = e.target.value;
intermediate();
};
<input type="text" onkeyup="betterFunction(event)" />
i hope that what you want :
let counter = 0;
// you need to define timer and newValue here first
let timer , newValue;
// defining input as varible for good usage better than usage in html
var input = document.querySelector("#inp");
const getData = () => {
// increment first better than in console :)
counter+=1;
console.log("Fetching Data .." , newValue , counter);
// as last step clear timer for next timeout
clearTimeout(timer);
}
// givin value direct to timer directlly worked better than return
const debounce = function (fn, d) {
timer = setTimeout(fn, d);
}
const betterFunction = () => {
// newvalue must equal input value
newValue = input.value;
// and then calling debounce as last step
debounce(getData, 2000);
}
// here giving onkeyup event to input for getting values and start working :)
input.onkeyup = betterFunction;

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

setTimeout pass itself as an argument

I want to pass the ID of the current timeout to the function it is executing.
function test(timeout){
console.log(timeout)
}
setTimeout(test,1000,"timeout object") //how to pass a refrence to the current timeout object or id
edit:
timeout may be an object (ES6) or a number, anyway I want a reference to it
I don't want to declare timeout as a global variable, I just want to pass it as an argument
I would create an utility function and then use it any time I need it.
Also #Velimir Tchatchevsky answer is the best to use. The wrapper soluce must be way overkill.
function setTimeoutWrapper(func, time) {
const ref = setTimeout(() => {
func(ref);
}, time);
}
setTimeoutWrapper((timeoutReference) => {
console.log('timeoutReference = ', timeoutReference);
}, 1000);
Also I don't see the point of passing the reference to the function, because if you are into the function, it means it get called. So there is no need for clearTimeout there.
Because overkill is fun :
function setTimeoutWrapper(func, time, ...args) {
const ref = setTimeout(() => {
func(ref, ...args);
}, time);
}
setTimeoutWrapper((timeoutReference, p1, p2) => {
console.log('timeoutReference = ', timeoutReference);
console.log('p1 = ', p1);
console.log('p2 = ', p2);
}, 1000, 'I am a parameter', 'second param');
What about some IIFE and no global variable?
(() => {
const ref = setTimeout(() => {
console.log('timeoutReference = ', ref);
}, 1000);
})();
console.log('Is there a global variable ?', ref);
With an outside function
function func(ref) {
console.log('reference : ', ref);
}
(() => {
const ref = setTimeout(() => func(ref), 1000);
})();
console.log('Is there a global variable ?', ref);
var myTimeout = setTimeout(test, 1000, "timeout object")
function test(timeout) {
console.log(myTimeout);
}
Now that myTimeout is a reference to your timeout you can use it to cancel it with window.clearTimeout(myTimeout) or use it for whatever purpose you want.
You can store the timeout reference in a variable which will be available inside of the passed function.
var timeoutReference = setTimeout(function(){
console.log('do something with the timeout reference');
console.log('timeoutReference = ',timeoutReference);
}, 1000);
Technically the setTimeout() function returns an id, store it in a variable and pass it over.
function test(timeout) {
console.log(timeout2)
}
var timeout2 = setTimeout(test, 1000)
Edit: Depending on the environment you are executing it in, sometimes you can't use a variable before declaring it (i.e ES6 Class). You'll have to take care of that.
You can achieve this by manipulating the this context of your test() function being called by the setTimeout. You want to use an object property rather than a variable to pass in the timeout reference because the execution will happen asynchronously.
function delay(){
var obj = {};
obj.timeout = setTimeout(test.bind(obj), 1000)
}
function test() {
console.log(this.timeout);
}
delay();
Alternatively, you can just pass the object in as the third parameter to the setTimeout function, like so:
function delay(){
var obj = {};
obj.timeout = setTimeout(test, 1000, obj)
}
function test(param) {
console.log(param.timeout);
}
delay();

Categories

Resources