Related
generally for js debounce function, a simple implement goes like
function debounce(func, wait) {
let timerId = null;
return function (...args) {
console.log('args',args);
clearTimeout(timerId);
timerId = setTimeout(() => func.apply(this, args), wait);
}
}
and when you use it, you can do things like
var random = function() {console.log(arguments)};
var test = debounce(random, 1000);
test(1,2,3);
my question is, how does the returned function inside debounce function can get all attributes that gets passed into test function (here being 1,2,3) through arguments object? I feel like it might have to do with closure, but can anyone explain?
I've also created a jsFiddle for simpler view
https://jsfiddle.net/u4n07veb/22/
Another question would be in this js fiddle, my console.log args can print 1,2,3, since 1,2,3 is what I pass to test function, but would it also be possible to get 4,5,6 inside the debounce function? Since 4,5,6 is the parameters I pass to the callback function of the debounce function?
Though the arguments special object does exist in JavaScript, in the implementation of debounce above the function arguments are instead retrieved via a rest parameter.
The difference is minimal - arguments is not a true Array but instead an Array-like object, whereas the ...args rest parameter method will retrieve an actual Array - but it's one worth mentioning.
The actual passing through of these arguments happens when Function.prototype.apply is called, which allows a function to be called with a given value of this and a specified array of arguments. It's a sibling to the similar Function.prototype.call, which takes each argument to passed through as a separate parameter.
So in your example, you call test(1, 2, 3) and that executes the function that was returned by debounce(random, 1000). That function gets its arguments as an Array via the ...args rest parameter, and then passes that array through to the random function via func.apply(this, args).
To answer your question about passing a different set of parameters through, I recommend you try it and see. But the answer is yes: with this setup the debounced function is able to pass through any number of arguments.
Closures aren't directly relevant to how the arguments are passed through here. They are relevant in a different way, however, in that the timerId variable created when debounce is called is kept in a closure so that later attempts to call the debounced function will access it again, which is what allows the innards of this debounce implementation to clear the timeout it had created during its previous execution.
Up to this point, I thought "calling" and "invoking" a function meant the same thing. However, in a YouTube tutorial it said to invoke a function by calling it. My first thought was that the wording was a mistake, but on W3Schools' page on Function Invocation, it says:
It is common to use the term "call a function" instead of "invoke a
function" ... In this tutorial, we will use invoke, because a
JavaScript function can be invoked without being called.
Okay, so there's a difference. What is it?
Your reference text:
It is common to use the term "call a function" instead of "invoke a
function" ... In this tutorial, we will use invoke, because a
JavaScript function can be invoked without being called.
Now let me rephrase it:
It is common to use the term "call a function" instead of "invoke a
function" ... In this tutorial, we will use the term invoke instead of call, because a
JavaScript function can be invoked indirectly like fn.call() and fn.apply() without being called directly like fn().
So, when I do fn(), it's invoked directly and when I do it like fn.call(), it's invoked indirectly but in both cases, the function is being invoked. Otherwise, I see no difference here and I can also say that I can call a function in many ways, for example:
fn(); // I'm calling it
fn.call(); // I'm calling it
fn.apply(); // I'm calling it
So, the difference is semantic but both are interchangeable, IMO. BTW, I wrote a comment above, under the question and I would like to put it here, which is:
IMO, that's a misleading statement. Maybe there are some indication of
call/apply or something else but it's totally confusing.
The difference is semantic and subtle. When you call a function, you are directly telling it to run. When you invoke a function, you are letting something run it.
There is one way to call a function:
myFunction()
Here, you are invoking the function (letting it run) by calling it directly.
There are many ways to invoke a function (given throughout different comments and answers). Here's one example:
function invoker(functionName) {
functionName()
}
invoker(myFunction)
Here, by calling invoker, you are invoking myFunction, which is being called indirectly.
Yes, in most cases we use both to refer the execution of a function.
There are 2 ways to reach place B from your HOME.
Direct/Automatic way (Invoke), i.e. if you choose first way, you do not need to walk. Someone will automatically take you to place B.
Indirect way (Call), i.e. if choose second way, you only need to reach A(by walk). Someone is there at place A to automatically take you to place B.
Have a look at the below image. I think it will clear your doubt.
In case of Calling, you originally refer to the statement that actually calls the function.
In case of Invoking, you indirectly refer to calling statement to actually invoke/run the function.
Many people use the term calling and invoking interchangeably but that's not right. There is a very slight difference between calling and invoking a function. In JavaScript functions can be invoked without being called which means that the code inside the body of the function can be executed without creating an object for the same. It is tied to the global object. When there is no individual object, the value of this is associated with the global object.
There is also a difference between call and apply, the fundamental difference is that call() accepts an argument list, while apply() accepts a single array of arguments. A different this object can be assigned when calling an existing function. this refers to the current object, the calling object. With call, you can write a method once and then inherit it in another object, without having to rewrite the method for the new object.
So, the major difference between invoking and calling comes in terms of the this object. Calling let's you set the this value whereas invoking just ties it to the global object.
"function invoked" means a function got executed
"function called" means that a function was called by another function and then executed
"function invoked without being called" is a function that got self invoked without being called by another function
example of a self invoking function calling another function:
var f = (function() {
foo();
})(); ///() here means it self invoked -- without being called directly.
var foo = (function() {
///Do something here
});
This question already has answers here:
What is the difference between a function call and function reference?
(6 answers)
Closed 12 months ago.
In javascript there is a callback, and functions are first-class objects right? so i can pass a function as an argument however if i pass console.log() it doesn't work why is that isn't it a function as well?
setTimeout(function(){console.log("hello")},5000);
this code is valid however
setTimeout(console.log("hello"),5000);
produces an error, why is that?
When you call console.log with some argument, the argument is printed to console and the function returns undefined.
So when you do:
setTimeout(console.log("hello"),5000);
"hello" will be printed, but what you're essentially doing is:
setTimeout(undefined, 5000);
In the other example (that works) you create a new function, but you do not call it. So you pass this new function to setTimeout, and that is why it works.
The reason that the following code
setTimeout(console.log("hello"),5000);
fails is because console.log() is invoked directly inside the setTimeout parameter and it always returns undefined (more info: MDN Documentation on Console.log). This means you are essentially running setTimeout(undefined,5000);
You need to provide a function declaration that is not invoked. If you wrap the code inside of a function declaration and do not invoke it, setTimeout will invoke it for you after the specified time lapses. This is why your first statement works, you provide a function declaration:
setTimeout(function(){console.log("hello")},5000);
If you had invoked the function you provided in the first parameter it would also return undefined and "hello" would be output immediately.
So if you provide a function declaration it will work:
setTimeout(()=>{console.log("hello")},5000);
Another way to use console.log directly without a function declaration is to bind it to a variable (more info: MDN Documentation on Function.prototype.bind). An example using .bind prototype:
setTimeout(console.log.bind(null,"hello"),5000);
The code above binds "hello" to invocation of console.log. The first parameter is null in this example but it is the this context.
setTimeout also allows you to pass variables that you want the function to be invoked with. See MDN Documentation on setTimeout
For example to pass a variable:
setTimeout(console.log,5000,'hello');
In the above example you are telling setTimeout to invoke console.log in 5 seconds with the variable (in this case a sting) of 'hello'.
Invoking console.log('hello') will return undefined so you are not really passing it to setTimeout, It will print "hello" though but not inside a callback.
In most cases it wont throw an error (as you can see in the example below).
What you can do however is pass console.log (the function) and a 3rd argument the string "hello" in our case.
Running example with all 3 cases:
setTimeout(console.log("hello"),500);
setTimeout(function(){console.log("hello2")},500);
setTimeout(console.log,500,"hello3");
It produces an error since it evaluates console.log(...), which evaluates to undefined, and thus is not a function.
setTimeout accepts the function as a parameter, not the function call. console.log() is basically invoking the function/method but setTimeout requires the reference or more specifically called the callback.
In your example:-
setTimeout(function(){console.log("hello")},5000);
you can call it like
var callback = function(){console.log("hello")};
setTimeout(callback,5000);
The callback will be called by setTimeout later in future. I hope it clears everything.
In JavaScript, the setTimeout function requires code to be enclosed in a function.
Examples of invalid timeouts:
window.setTimeout(console.log('PING'),3000)
function ping(){console.log('PING')};window.setTimeout(ping(),3000)
Example of valid timeouts:
window.setTimeout(function(){console.log('PING')},3000)
function ping(){console.log('PING')};window.setTimeout(function(){ping()},3000)
Now my question: why? I understand why normal code might need to be enclosed in a function, but why is it ALWAYS necessary to enclose code in function(){}?
It doesn't always require an anonymous function. You can also pass a function reference as the first argument, for example, let's assume you have a function called log defined. You can validly write:
function log()
{
console.log( 'PING' );
}
window.setTimeout( log, 200 );
Notice that we don't pass the parentheses with the first argument here.
However, you're not able to pass parameters directly to log() in this instance, so it's necessary to wrap the function call inside an anonymous function.
The code is required to be enclosed in a function because, the setTimeout function does not execute individual lines of code. It takes two, arguments - the first argument is a callback function, and the second argument is the time in milliseconds. The callback function will be called by the setTimeout function internally after the specified amount of time passes.
In the example you gave
window.setTimeout(function(){console.log('PING')},3000)
you pass an anonymous function which would be called after 3000 milliseconds or 3 seconds.
Basically because setTimeout is an asynchronous operation and you need to give what to do next once the timeout is done (i.e. you give a callback function).
Unless JavaScript runtime could block the UI thread and resume execution once the setTimeout ends, you need to provide some executable code to perform an action once the asynchronous operation has been completed:
setTimeout(function() {
// Do stuff after 1 second
}, 1000);
In the other hand, what would be the point of giving a function return value as argument? Check this wrong code to be more illustrative:
// You do some action even BEFORE the timeout operation has been completed?
setTimeout(doStuff(), 1000);
Further reading
Callbacks in Wikipedia
It doesn't need to be enclosed in function(){} but it does need to be a parameter of type Function.
In the case of window.setTimeout(console.log('PING'),3000), what would happen is that console.log() would immediately be executed and the return value (which is undefined) would be passed to the setTimeout function. This code isn't passing a function as a parameter, it's passing the return value of a function as a parameter. Essentially, it's just a shorter way of writing this:
var retVal = console.log('PING'); // retVal === undefined
window.setTimeout(retVal,3000);
That's not special to setTimeout. console.log without () is a function, but console.log() means to invoke that function.
There are other methods to pass a function in to setTimeout, but the anonymous function is typically the cleanest.
Technically, this would work, too:
window.setTimeout(console.log,3000)
but it would not allow you to specify a parameter, making it rather useless here. This could be avoided by binding parameters:
window.setTimeout(console.log.bind(null,'PING'),3000)
In this case, bind is a function which is being invoked (as you can see by the fact it has parameters supplied), so just as before bind is immediately executed. However, bind is a function whose return value is itself a function, and it's that returned function that is passed to setTimeout and called three seconds later. This technique (which you will see used with bind, call, and apply) is called partial application which really just means that you transform a function taking some parameters into a function taking fewer parameters (in this case, zero parameters) by specifying the parameters now but executing the function later.
Because setTimeout is a function that takes two argument - a function, and a number of seconds before that function executes.
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
Parameters:
func: A function to be executed after the timer expires.
code: An
optional syntax allows you to include a string instead of a function,
which is compiled and executed when the timer expires. This syntax is
not recommended for the same reasons that make using eval() a security
risk.
delay: Optional The time, in milliseconds (thousandths of a
second), the timer should wait before the specified function or code
is executed. If this parameter is omitted, a value of 0 is used. Note
that the actual delay may be longer; see Reasons for delays longer
than specified below.
param1, ..., paramN Optional Additional
parameters which are passed through to the function specified by func
once the timer expires.
Apparently YOU can just use code, as per the second argument above, but I have never seen it done.
Simply put...
why does
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
work perfectly, calling the function after the the specified delay, but
setTimeout(playNote(currentaudio.id,noteTime), delay);
calls the function playNote all at the same time?
(these setTimeout()s are in a for loop)
or, if my explanation is too hard to read, what is the difference between the two functions?
The first form that you list works, since it will evaluate a string at the end of delay. Using eval() is generally not a good idea, so you should avoid this.
The second method doesn't work, since you immediately execute a function object with the function call operator (). What ends up happening is that playNote is executed immediately if you use the form playNote(...), so nothing will happen at the end of the delay.
Instead, you have to pass an anonymous function to setTimeout, so the correct form is:
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
Note that you are passing setTimeout an entire function expression, so it will hold on to the anonymous function and only execute it at the end of the delay.
You can also pass setTimeout a reference, since a reference isn't executed immediately, but then you can't pass arguments:
setTimeout(playNote, delay);
Note:
For repeated events you can use setInterval() and you can set setInterval() to a variable and use the variable to stop the interval with clearInterval().
You say you use setTimeout() in a for loop. In many situations, it is better to use setTimeout() in a recursive function. This is because in a for loop, the variables used in the setTimeout() will not be the variables as they were when setTimeout() began, but the variables as they are after the delay when the function is fired.
Just use a recursive function to sidestep this entire problem.
Using recursion to deal with variable delay times:
// Set original delay
var delay = 500;
// Call the function for the first time, to begin the recursion.
playNote(xxx, yyy);
// The recursive function
function playNote(theId, theTime)
{
// Do whatever has to be done
// ...
// Have the function call itself again after a delay, if necessary
// you can modify the arguments that you use here. As an
// example I add 20 to theTime each time. You can also modify
// the delay. I add 1/2 a second to the delay each time as an example.
// You can use a condition to continue or stop the recursion
delay += 500;
if (condition)
{ setTimeout(function() { playNote(theID, theTime + 20) }, delay); }
}
Try this.
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
Don't use string-timeouts. It's effective an eval, which is a Bad Thing. It works because it's converting currentaudio.id and noteTime to the string representations of themselves and hiding it in the code. This only works as long as those values have toString()s that generate JavaScript literal syntax that will recreate the value, which is true for Number but not for much else.
setTimeout(playNote(currentaudio.id, noteTime), delay);
that's a function call. playNote is called immediately and the returned result of the function (probably undefined) is passed to setTimeout(), not what you want.
As other answers mention, you can use an inline function expression with a closure to reference currentaudio and noteTime:
setTimeout(function() {
playNote(currentaudio.id, noteTime);
}, delay);
However, if you're in a loop and currentaudio or noteTime is different each time around the loop, you've got the Closure Loop Problem: the same variable will be referenced in every timeout, so when they're called you'll get the same value each time, the value that was left in the variable when the loop finished earlier.
You can work around this with another closure, taking a copy of the variable's value for each iteration of the loop:
setTimeout(function() {
return function(currentaudio, noteTime) {
playNote(currentaudio.id, noteTime);
};
}(currentaudio, noteTime), delay);
but this is getting a bit ugly now. Better is Function#bind, which will partially-apply a function for you:
setTimeout(playNote.bind(window, currentaudio.id, noteTime), delay);
(window is for setting the value of this inside the function, which is a feature of bind() you don't need here.)
However this is an ECMAScript Fifth Edition feature which not all browsers support yet. So if you want to use it you have to first hack in support, eg.:
// Make ECMA262-5 Function#bind work on older browsers
//
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
if (arguments.length<=1) {
return function() {
return that.apply(owner, arguments);
};
} else {
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
};
}
};
}
I literally created an account on this site to comment on Peter Ajtai's answer (currently highest voted), only to discover that you require 50 rep (whatever that is) to comment, so I'll do it as an answer since it's probably worth pointing out a couple things.
In his answer, he states the following:
You can also pass setTimeout a reference, since a reference isn't executed immediately, but then you can't pass arguments:
setTimeout(playNote, delay);
This isn't true. After giving setTimeout a function reference and delay amount, any additional arguments are parsed as arguments for the referenced function. The below would be better than wrapping a function call in a function.
setTimeout(playNote, delay, currentaudio.id, noteTime)
Always consult the docs.
That said, as Peter points out, a recursive function would be a good idea if you want to vary the delay between each playNote(), or consider using setInterval() if you want there to be the same delay between each playNote().
Also worth noting that if you want to parse the i of your for loop into a setTimeout(), you need to wrap it in a function, as detailed here.
It may help to understand when javascript executes code, and when it waits to execute something:
let foo2 = function foo(bar=baz()){ console.log(bar); return bar()}
The first thing javascript executes is the function constructor, and creates a function object. You can use either the function keyword syntax or the => syntax, and you get similar (but not identical) results.
The function just created is then assigned to the variable foo2
At this point nothing else has been run: no other functions called (neither baz nor bar, no values looked up, etc. However, the syntax has been checked inside the function.
If you were to pass foo or foo2 to setTimeout then after the timeout, it would call the function, the same as if you did foo(). (notice that no args are passed to foo. This is because setTimeout doesn't by default pass arguments, although it can, but those arguments get evaluated before the timeout expires, not when it expires.)
After foo is called, default arguments are evaluated. Since we called foo without passing arguments, the default for bar is evaluated. (This would not have happened if we passed an argument)
While evaluating the default argument for bar, first javascript looks for a variable named baz. If it finds one, it then tries to call it as a function. If that works, it saves the return value to bar.
Now the main body of the function is evaluated:
Javascript looks up the variable bar and then calls console.log with the result. This does not call bar. However, if it was instead called as bar(), then bar would run first, and then the return value of bar() would be passed to console.log instead. Notice that javascript gets the values of the arguments to a function it is calling before it calls the function, and even before it looks up the function to see if it exists and is indeed a function.
Javascript again looks up bar, and then it tries to call it as a function. If that works, the value is returned as the result of foo()
So, function bodies and default arguments are not called immediately, but everything else is. Similarly, if you do a function call (i.e. ()), then that function is executed immediately as well. However, you aren't required to call a function. Leaving off the parentheses will allow you to pass that function around and call it later. The downside of that, though, is that you can't specify the arguments you want the function to be called with. Also, javascript does everything inside the function parentheses before it calls the function or looks up the variable the function is stored in.
Because the second one you're telling it to call the playNote function first and then pass the return value from it to setTimeout.