this is probably a simple mistake. I'm trying to implement a throttle function (Credit to our man webdevsimplified for quality content: https://blog.webdevsimplified.com/2022-03/debounce-vs-throttle/).
My throttle implementation is not working and I am not sure why. How can I get the throttle working?
function printHi() {
console.log("Hi");
}
function throttle(cb, delay = 1000) {
let shouldWait = false
let waitingArgs
const timeoutFunc = () => {
if (waitingArgs == null) {
shouldWait = false
} else {
cb(...waitingArgs)
waitingArgs = null
setTimeout(timeoutFunc, delay)
}
}
return (...args) => {
console.log("should wait?", shouldWait);
if (shouldWait) {
// It never goes in here...?
waitingArgs = args
return
}
cb(...args)
shouldWait = true
setTimeout(timeoutFunc, delay)
}
}
<button onclick="(function () {
throttle(printHi)();
})()">Click me</button>
Consecutive button clicks print to the console:
should wait? false
Hi
should wait? false
Hi
should wait? false
Hi
shouldWait is never printed true, even though it should...
Your original implementation didn't work because shouldWait and waitingArgs were scoped to the function, so every function run had a fresh set of these variables with shouldWait = false.
You might have made this mistake due to the scope differences between var and let, the former was globally scoped if used this way.
Here is my solution, which simply moved the 2 variables out of the function.
function printHi() {
console.log("Hi");
}
let shouldWait = false
let waitingArgs
function throttle(cb, delay = 1000) {
const timeoutFunc = () => {
if (waitingArgs == null) {
shouldWait = false
} else {
cb(...waitingArgs)
waitingArgs = null
setTimeout(timeoutFunc, delay)
}
}
return (...args) => {
console.log("should wait?", shouldWait);
if (shouldWait) {
// It never goes in here...?
waitingArgs = args
return
}
cb(...args)
shouldWait = true
setTimeout(timeoutFunc, delay)
}
}
<body>
<button onclick="(function () {
throttle(printHi)();
})()">Click me</button>
</body>
Related
Let's say there is a code in place 2
var place2IsReady = true;
In place 1 I need to implement the logic below :
Once place2IsReady value was changed (to true) then display alert('ready!');
Notes:
place2IsReady variable is not available in the scope of place 1.
the code from place 1 gets executed before place 2 gets executed (or there is a race condition).
Solution 1
I believe I can use window.place2IsReady instead and use setTimeout/setInterval in place 1 until I get window.place2IsReady === true.
Any better options? Using Listeners? On the variable change?
P.S. I need to track only first possible change of place2IsReady.
Is there a better way? Thank you.
You can create a listener for the variable change using setTimeout, something like:
let place2IsReady = false;
setReadyListener();
// testing wait 2 seconds to set place2IsReady to true
// so: an alert should occur after 2 seconds
setTimeout(() => place2IsReady = true, 2000);
function setReadyListener() {
const readyListener = () => {
if (place2IsReady) {
return alert("Ready!");
}
return setTimeout(readyListener, 250);
};
readyListener();
}
A more generic listener 'factory' could be:
let place2IsReady = false;
let fromObj = {
place2IsReady: "busy",
done() { this.place2IsReady = "done"; },
};
const listen = changeListenerFactory();
listen(
() => place2IsReady,
() => console.log("place2IsReady") );
listen(
() => fromObj.place2IsReady === "done",
() => console.log("formObj.place2IsReady done!") );
console.log("Listening...");
// test change variables with listeners
setTimeout(() => place2IsReady = true, 1000);
setTimeout(() => fromObj.done(), 3000);
function changeListenerFactory() {
const readyListener = (condition, callback, delay) => {
if (!condition || typeof condition !== "function") { return true; }
if (condition()) {
return callback();
}
setTimeout(() => readyListener(condition, callback, delay), delay);
};
return (condition, callback = () => {}, delay = 250) =>
readyListener(condition, callback, delay);
}
Or maybe using a Proxy (with a set trap) works for you
const readyState = new Proxy({ ready: false }, {
set (target, prop, val) {
console.log(`readyState.ready changed from ${target[prop]} to ${val}`);
target[prop] = val;
}
});
console.log("Waiting for changes ...");
setTimeout(() => readyState.ready = true, 2000);
Assuming you can replace place2IsReady with an object:
place2IsReady = {
state: false,
set ready(value) {
this.state = value
state && place_1_call()
},
get ready() {
return state
}
}
place_1_call = () => {
alert('ready')
}
place2IsReady.ready = true
I want most understandable syntax for polling a flag and return when it is true, my code snippet below doesn't work I know, what's the syntax that would make it work if you get my idea ?
async function watch(flag) {
let id = setInterval(function(){
if (flag === true) {
clearInterval(id);
}
}, 1000);
return flag;
}
If you want to poll a variable where the value is a primative, then you need to define it outside the function, otherwise it can't change.
If you want to have a promise resolve when that condition is done, then you have to create it explicitly. async and await are tools for managing existing promises.
let flag = false;
function watchFlag() {
return new Promise(resolve => {
let i = setInterval(() => {
console.log("Polling…");
if (flag) {
resolve();
clearInterval(i);
}
}, 500);
});
}
setTimeout(() => {
flag = true;
}, 1500);
console.log("Watching the flag");
watchFlag().then(() => {
console.log("The flag has changed");
});
If you don't know when the flag is going to change (in 10 seconds or in 10 minutes), you can use a setter instead. Probably an anti-pattern, but again your question doesn't really show us how you would be using this flag in your code.
const flagsObject = {
set flag(stat) {
this._flag = stat;
if (stat) {
// call the function you need to call when flag is true
// you could add additional condition if you only want to run the function
// when the flag is switched
doSomething()
}
},
get flag() {
return this._flag;
}
};
flagsObject.flag = true; // doSomething() will be called
I want to lock an async function from simultaneous execution. The function can be only executed by one caller and it should get true as output after execution where others get false as the return without executing the logical content of the function. Others should not wait for the current caller to complete.
This is what I tried so far:
class Lock {
constructor() {
this.locked = false;
}
lock(resolve) {
if (this.locked) {
resolve(false)
}
this.locked = true
return
}
release(resolve) {
this.locked = false;
resolve(true)
}
}
let lock = new Lock();
function myFunction() {
return new Promise(resolve => {
lock.lock(resolve);
//do something - this takes time includes some api calls and db operations
lock.release(resolve);
})
}
async function callSite() {
const executed = await myFunction();
if(executed){
//do something
}
else{
//do another thing
}
}
But seems like it doesn't work as expected. Can anyone help me to improve this?
Promise execution is not always guaranteed to stop immediately after resolving or rejecting; you'll have to return a boolean after you check the lock and exit or continue the promise accordingly.
Run the below snippet; it's an example having 3 buttons with the same promise and a 3 seconds delay. When one button is running all the others can not execute.
class Lock {
constructor() {
this.locked = false;
}
lock(resolve) {
if (this.locked) {
resolve(false);
return false;
}
this.locked = true
return true;
}
release(resolve) {
this.locked = false;
resolve(true)
}
}
let lock = new Lock();
function myFunction({ resultEl }) {
resultEl.textContent = "Is running";
return new Promise(resolve => {
// Check if it's locked
if (!lock.lock(resolve)) {
// If it is, return and exit the function even if the promise is already resolved
return;
}
// do something - this takes time includes some api calls and db operations
// just wait 3 seconds for demontration
setTimeout(() => {
lock.release(resolve);
}, 3000);
})
}
async function callSite() {
this.disabled = true;
const executed = await myFunction({ resultEl : this.nextSibling });
this.disabled = false;
if (executed) {
this.nextSibling.textContent = `Finished at ${(new Date).getTime()}`;
}
else {
this.nextSibling.textContent = `Was NOT executed at ${(new Date).getTime()}`;
}
}
document.getElementById('a-button').addEventListener('click', callSite);
document.getElementById('b-button').addEventListener('click', callSite);
document.getElementById('c-button').addEventListener('click', callSite);
div {
margin-bottom: 10px;
}
button {
margin-right: 5px;
}
<div>
<button id="a-button">Run A</button><span></span>
</div>
<div>
<button id="b-button">Run B</button><span></span>
</div>
<div>
<button id="c-button">Run C</button><span></span>
</div>
I'm trying to create a custom debounce function:
const debounced = [];
const cancelFunc = timeout => () => {
clearTimeout(timeout);
};
function debounce(fn, wait, ...args) {
let d = debounced.find(({ func }) => func === fn);
if (d) {
d.cancel();
} else {
d = {};
debounced.push(d);
}
d.func = fn;
d.timeout = setTimeout(fn, wait, ...args);
d.cancel = cancelFunc(d.timeout);
}
If I use with a named function, it works as intended:
debounce(foo, 1000); // called once with 5 clicks in 1 second
But I can't get it to work with anonymous functions:
debounce(() => { foo(5); }, 1000); // called 5 times with 5 clicks in 1 second
I created a pen here: https://codepen.io/anon/pen/gQvMdR?editors=1011
This happens because of your find condition. Let's back up, and consider this bit of code:
if (
(function(){ return 1 }) === (function(){ return 1 })
) {
console.log('The functions are equal');
} else {
console.log('The functions are NOT equal');
}
// logs 'The functions are NOT equal'
Even though I wrote two identical anonymous functions, they are not strictly equal to each other. When you pass in that anonymous function, that is essentially what you are doing. So, when you search for your array for a previously found function, it will never find a match, because each time debounce(() => { foo(5); }, 1000); is called it creates a new function. Since it'll never find a match, it will never be canceled.
As mentioned by #SLaks "Each call creates a separate function, so you won't find it in the array."
So you just need to store something in the array to match it to, you can use .toString()
// ================
const debounced = [];
const cancelFunc = timeout => () => {
clearTimeout(timeout);
};
function debounce(fn, wait, ...args) {
let d = debounced.find(({ funcString }) => funcString === fn.toString());
if (d) {
d.cancel();
} else {
d = {};
debounced.push(d);
}
d.func = fn;
d.funcString = fn.toString()
d.timeout = setTimeout(fn, wait, ...args);
d.cancel = cancelFunc(d.timeout);
}
// ================
function foo(value) {
console.log('value:', value)
}
function onClickBroken() {
debounce(() => { foo(5); }, 1000);
}
<button onClick="onClickBroken()">Click me 5 times</button>
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>