SetTimeout recursive with arrow function - javascript

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.

Related

Trying to use setInterval to make a throttling function unsuccessfully

I am trying to return a function that only invokes a callback function 'func' once per every 'wait' milliseconds.
Additional calls to the callback 'func' within the 'wait' period should NOT be invoked or queued.
This is what I have so far...
function throttle(func, wait) {
function inner(...args) {
setInterval(func(...args), wait);
}
return inner;
}
When I run the code through the test algorithm I get the following errors:
"throttled functions should only be able to be called again after the specified time"
Here is the testing algorithm...
let counter = 0;
const incr = () => counter++;
const throttledIncr = throttle(incr, 32);
throttledIncr();
throttledIncr();
setTimeout(() => {
expect(counter).to.eql(1);
throttledIncr();
setTimeout(() => {
expect(counter).to.eql(2);
done();
}, 32);
}, 32);
"throttled functions return their value"
Here is the testing algorithm...
let counter = 0;
const incr = () => ++counter;
const throttledIncr = throttle(incr, 32);
const result = throttledIncr();
setTimeout(() => {
expect(result).to.eql(1);
expect(counter).to.eql(1);
done();
}, 64);
"throttled functions called repeatedly should adhere to time limitations"
Here is the testing algorithm...
const incr = () => ++counter;
const throttledIncr = throttle(incr, 64);
const results = [];
const saveResult = () => results.push(throttledIncr());
saveResult();
saveResult();
setTimeout(saveResult, 32);
setTimeout(saveResult, 80);
setTimeout(saveResult, 96);
setTimeout(saveResult, 180);
setTimeout(() => {
expect(results[0]).to.eql(1);
expect(results[1]).to.be(undefined);
expect(results[2]).to.be(undefined);
expect(results[3]).to.eql(2);
expect(results[4]).to.be(undefined);
expect(results[5]).to.eql(3);
done();
}, 192);
My questions regarding each case:
How do I prevent the function from being called again ?
Why ISNT my function returning value? I can't deduce what or how to return a value with the given testing algorithm.
What does "throttled functions called repeatedly should adhere to time limitations" even mean? This seems contradictory to the first error. There isn't any mention of setting a time limit so I don't believe using setTimeout here is what they mean...
How do I prevent the function from being called again ?
function throttle(func, wait) {
function inner(...args) {
setInterval(func(...args), wait);
}
return inner;
}
First, your code above does not do what you expect it to do. Currently every time you invoke throttle, you are adding func to the event loop, to be executed on an interval.
So when you call throttleIncr 5 times, you are adding incr to the eventloop to be called five times.
One approach (imo), would be to keep track of the last time that throttle(func) was invoked. The next time throttle(func) is invoked, check to see if the wait time has elapsed. If so, invoke func and save off the new time. If not, return.
Why ISNT my function returning value? I can't deduce what or how to return a value with the given testing algorithm.
Your incr function, IS returning the value, however your throttle function puts it on the eventloop, for asychronous execution, so the return value is not available.
What does "throttled functions called repeatedly should adhere to time limitations" even mean? This seems contradictory to the first error.
This is not a javascript error, and likely a custom failure message from the tests you are invoking.
I tried something here, that seems to be working:
function throttle2(callback, delay = 1000) {
let interval;
let currentArgs;
return (...args) => {
currentArgs = args;
if (!interval) {
interval = setInterval(() => {
if (currentArgs) {
callback(...currentArgs);
currentArgs = null;
} else {
clearInterval(interval);
interval = false;
}
}, delay);
}
};
}
Sandbox: https://codesandbox.io/s/competent-tereshkova-ccop2e?file=/index.js:167-179

function call within setTimeout not executing

I am executing the function delayFilter() on keyup of an input box. I want to delay 1 second after the user stops typing and run the function filterProducts(). However, when executing filterProducts() inside of the setTimeout I get the console error "this.filterProducts is not a function". This function gets called fine when outside of the setTimeout. Why is this error being thrown?
delayFilter() {
let timeout = null;
clearTimeout(timeout);
timeout = setTimeout(function() {
this.filterProducts();
}, 1000);
}
filterProducts() {
//do stuff
}
That is because these, this inside the callback, does not refer to the object outside.
Try this:
delayFilter() {
let timeout = null;
clearTimeout(timeout);
let self = this;
timeout = setTimeout(function() {
self.filterProducts();
}, 1000);
}
filterProducts() {
//do stuff
}
You can also try the arrow function. The reason can be seen here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords.
delayFilter() {
let timeout = null;
clearTimeout(timeout);
timeout = setTimeout(() => {
this.filterProducts();
}, 1000);
}
filterProducts() {
//do stuff
}
You need to bind the scope of the function in the setTimeout callback. the easiest way is to use arrow functions, if your platform supports it.
timeout = setTimeout(() => this.filterProducts(), 1000);
you can also save the scope in a variable.
var self = this;
timeout = setTimeout(funciton() { self.filterProducts() }, 1000);
Alternatively you can manually bind it. This is useful if you want to pass the function around.
timeout = setTimeout(function() {...}.bind(this), 1000);

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

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

I can't change global variable inside setInterval es6

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

Categories

Resources