I came across this code while learning about yield and wondering what yield would do as a argument to a function . Looks like a glorified return in this place
export function * throttle(func, time) {
let timerID = null;
function throttled(arg) {
clearTimeout(timerID);
timerID = setTimeout(func.bind(window, arg), time); // what does this do????
}
while(true) throttled(yield);
}
export class GeneratorThrottle {
constuctor() {};
start = () => {
thr = throttle(console.log, 3000);
thr.next('');
};
toString = () => {
console.log(throttle);
console.log('start =', this.start);
};
};
With next method, You can pass data as argument into generator function.
function* logGenerator() {
console.log(0);
console.log(1, yield);
console.log(2, yield);
console.log(3, yield);
}
var gen = logGenerator();
// the first call of next executes from the start of the function
// until the first yield statement
gen.next(); // 0
gen.next('pretzel'); // 1 pretzel
gen.next('california'); // 2 california
gen.next('mayonnaise'); // 3 mayonnaise
And func.bind(window, arg) are just fancy way to call it with argument,
where bind method return a function...
And you can access this as window in that function...
Example:
function func(args) {
console.log(this, args)
}
func.bind("this", "args")();
Related
for example i have this program calling functions some with set time outs, some function work just calling them (setTimeout(this.siguienteNivel, 1500); but others needed (the last ones of the code) an arrow function, (​setTimeout(() =>​this.iluminarSecuenciaFinal();}, 1000 * i);) if the arrow function is missing the function does not trigger in these specific cases, i dont understand why this differentiator, the program is in a class, i dont know if that makes a difference
this is a piece, the complete code is this if needed:
https://github.com/moorooba/simon/blob/master/index.html
> elegirColor(ev) {
const nombreColor = ev.target.dataset.color;
const numeroColor = this.transformarColorANumero(nombreColor);
this.iluminarColor(nombreColor);
if (numeroColor === this.secuencia[this.subnivel]) {
this.subnivel++;
if (this.subnivel === this.nivel) {
this.nivel++;
this.eliminarEventosClick();
if (this.nivel === ULTIMO_NIVEL + 1) {
this.ganador();
} else {
setTimeout(this.siguienteNivel, 1500);
}
}
} else {
this.perdio();
}
}
// ganoElJuego() {
// swal("Platzi", "Felicitaciones, ganaste el juego!", "success").then(
// this.inicializar
// );
// }
// perdioElJuego() {
// swal("Platzi", "Lo lamentamos, perdiste :(", "error").then(() => {
// this.eliminarEventosClick();
// this.inicializar();
// });
// }
perdio() {
loser.classList.remove("hide");
setTimeout(this.startos, 1000);
this.eliminarEventosClick();
}
startos() {
loser.classList.add("hide");
boton.classList.remove("hide");
}
ganador() {
console.log("diparo");
this.eliminarEventosClick();
for (let i = 0; i < 4; i++) {
// setTimeout(this.iluminarSecuenciaFinal, 1000 * i);
setTimeout(() => {
this.iluminarSecuenciaFinal();
}, 1000 * i);
}
// setTimeout(this.ganoJuego, 4000);
setTimeout(() => {
this.ganoJuego();
}, 4000);
}
ganoJuego() {
console.log("ganojuego");
winner.classList.remove("hide");
// setTimeout(this.restart, 1500);
setTimeout(() => {
this.restart();
}, 1500);
}
You have to understand how this works in JavaScript:
class Foo {
bar = 'bar';
log() {
console.log(this.bar);
}
method() {
setTimeout(this.log);
setTimeout(() => this.log());
}
}
const obj = new Foo;
obj.method();
In this example, (which represents the relevant part of your code) the method function tries to call log in two different ways.
In the first one, it passes a reference to this.log as a parameter of setTimeout. The setTimeout function receives a function as a parameter and it has no idea of the context in which it has been declared, it's as if you extracted it from the class. In that case this is being evaluated when the function executes and it evaluates to window. The result is undefined because window.bar is undefined.
In the second one, it passes an arrow function whose particularity is to retain this when it is declared. When it executes, this refers to the instance of the class so the result is the same as if you called obj.log(). The result is 'bar' in that case.
Without using anything that has been deprecated and without passing as a parameter to the function call, can we return the entire function call with the exact parameters?
For example, we have:
function MyFunction(param1, param2, param3) {
var f = function() {
MyFunction(param1, param2, param3); // Made an edit here. This would make more sense.
}
return(f); // I really don't want to define f.
}
var capture = MyFunction(1, 2, 3);
capture();
Instead of hard coding the function call to the f variable, can we use some keyword or JavaScript function instead and thus require no maintenance?
Why not return another function inside function?
function MyFunction(param1, param2, param3) {
return function() {
return param1+param2+param3;
}
}
var capture = MyFunction(1, 2, 3);
capture();
You can use a generator function to call the function multiple times.
Using Promise.then
async function* func(...props) {
for (let i = 0; i < props.length; i++) {
const result = await new Promise((resolve) =>
setTimeout(() => resolve(props[i]), 1000)
);
yield result;
}
}
let generator = func(1, 2, 3);
generator.next().then((val) => {
console.log(val);
});
generator.next().then((val) => {
console.log(val);
});
generator.next().then((val) => {
console.log(val);
});
Using Async/Await
async function* func(...props) {
for (let i = 0; i < props.length; i++) {
const result = await new Promise((resolve) =>
setTimeout(() => resolve(props[i]), 1000)
);
yield result;
}
}
(async () => {
let generator = func(1, 2, 3);
console.log(await generator.next());
console.log(await generator.next());
console.log(await generator.next());
})();
This can't be fully generalized, a function can refer (only named functions) itself with its name, there's no common reference to the currently executed function. The snippet below does what you need, when you're having a named function, which is not a method of an object.
function captureFn(fn, args) {
return function() {
fn.call(null, ...args);
};
}
function MyFunction(param1, param2, param3) {
console.log(param1, param2, param3);
return captureFn(MyFunction, arguments);
}
var capture = MyFunction(1, 2, 3);
capture();
In the return statement of MyFunction you've to change the first argument to the name of the function on every function this return value is used.
This is easy to expand to cover object methods too. Just pass this as an argument to captureFn, and pass the passed this instead of null in fn.call. When calling from an object method, the function can be also anonymous, the function name can be replaced with a reference to the method (this.methodName). See this variation working at jsFiddle.
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>
Context: I was writing a simple throttle under the task of a javascript tutorial
Task: write a throttle that works like this:
function f(a) {
console.log(a)
};
// f1000 passes calls to f at maximum once per 1000 ms
let f1000 = throttle(f, 1000);
f1000(1); // shows 1
f1000(2); // (throttling, 1000ms not out yet)
f1000(3); // (throttling, 1000ms not out yet)
// when 1000 ms time out...
// ...outputs 3, intermediate value 2 was ignored
// P.S. Arguments and the context this passed to f1000 should be passed to the original f.
Here's my solution. Strangely, it works fine when I run it step-by-step in a debug console but not otherwise. Any idea why and how to fix it? (I assume it has to do with setTimeout?)
function throttle(f, ms) {
let isCoolDown = true,
queue = []
function wrapper(...args) {
queue.push(args)
if (!isCoolDown) return
isCoolDown = false
setTimeout(function() {
isCoolDown = true
if (queue[0] !== undefined) {
f.apply(this, queue.slice(-1))
queue = []
}
}, ms)
return function() {
f.apply(this, args)
queue = []
}()
}
return wrapper
}
A few things:
1) swap isCoolDown = false and isCoolDown = true
2) you don't need a queue, only one call has to go through the others get discarded with throttling
function throttle(fn, ms) {
let throttle = false;
let timer;
return wrapper(...args) {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
There is a bug at this line:
f.apply(this, queue.slice(-1))
The .slice method will return an array. Since the args is an array, the result of queue.slice(-1) would be something like:
[ [1, 2, 3] ]
Instead, you can change it to:
f.apply(this, queue.slice(-1)[0])
How can I create a custom chainable delay function in JavaScript using prototype or currying or using new keyword?
Original question is
var foo = function () {
console.log("bingo!");
};
function bar() {
console.log("Wow!");
}
// make below two lines work
foo.delay(300); // after 0.3s: "bingo!"
bar.delay(600); // after 0.6s: "Wow!"
// This returns undefined, how to fix it?
My attempt so far.
function delay(time) {
setTimeout(this, time);
}
var foo = (function () {
console.log();
console.log("bingo!");
return {
delay: delay.bind(this)
}
})();
function bar() {
console.log("Wow!");
return {
delay: delay.bind(this)
}
};
// bar.prototype.delay = function() { return console.log("bar.prototype.delay"); }
foo.delay(300); // after 0.3s: "bingo!"
bar.delay(600); // after 0.6s: "Wow!"
Lastly, can I ask you where would be a good place to study these kind of topics to be more fluent in JavaScript? Thank you in advance.
You have a problem with this code:
setTimeout(this, time); // The first param must be the function to be executed after `n` ms.
Here you need to pass the time value to the function delay, you could pass the implicit argumentsarray.
return {
delay: delay.bind(this) // Here you need to pass the `time` value.
}
This is an approach to bind the delay function.
return {
delay: delay.bind(this, function() {
console.log("Wow!");
}, ...arguments)
}
Important: You don't to bind the function delay to pass the context this value. That binding it's necessary if the function delay will use the context this.
function delay(fn, time) {
setTimeout(fn, time);
}
var foo = (function() {
return {
delay: delay.bind(this, function() {
console.log("bingo!");
}, ...arguments)
};
})();
var bar = (function() {
return {
delay: delay.bind(this, function() {
console.log("Wow!");
}, ...arguments)
};
})();
// bar.prototype.delay = function() { return console.log("bar.prototype.delay"); }
foo.delay(300); // after 0.3s: "bingo!"
bar.delay(600); // after 0.6s: "Wow!"