Need explanation about javascript function and callback - javascript

im learning javascript, and i have been following some video tutorial on youtube
this is the original code
function add(first, second, callback){
console.log(first+second);
callback();
}
function logDone(){
console.log("done");
}
add(2,3,logDone);
and the result of code on above is
5
main.js (line 4)
done
main.js (line 9)
and i make slight change to the code into this
function add(first, second, callback){
console.log(first+second);
callback;
}
function logDone(){
console.log("done");
}
add(2,3,logDone());
and the result is this
done
main.js (line 9)
5
main.js (line 4)
my question are:
could you explain to me why i got the result pile up that way?,
and what the difference if we call a function with the bracket () and without the bracket ()?

Explanation of the first snippet
function add(first, second, callback) {
console.log(first + second);
callback(); // run whatever the "callback" function is
}
function logDone() {
console.log("done");
}
add(2, 3, logDone); // pass in a function (not an invocation of a function) the
// function isn't run here
Explanation of the second snippet
function add(first, second, callback) {
console.log(first + second);
callback; // display the value of whatever "callback" is
}
function logDone() {
console.log("done");
}
add(2, 3, logDone()); // run "logDone" and then pass the result (which in this
// case is undefined) into add
As you can see, the first code snippet doesn't actually run the callback until in the add function, whereas the second snippet runs the callback before add so that it can pass whatever comes back from logDone into add.

Perhaps it becomes more clear to you when I alter the logDone declaration to this:
var logDone = function() {
console.log("done");
}
The identifier logDone basically is just a variable that references a function. To execute (also: call, or: invoke) the function you add parentheses: logDone().
So, in your first example you are merely passing the function itself as the third argument to add(), which then gets executed inside add(), with callback();.
In your second example, however, you are executing the function with logDone() immediately, which results in the return value of logDone()1 being passed as the third argument to the add() call. In other words, first logDone is executed (resulting in the first log message), then add is executed (resulting in the second log message).
Furthermore, the statement callback;, inside add, does not do anything. And if you would have left the parentheses just like in your first example, it would result in an error, because undefined2 is not a function.
1) which is undefined in this case, because logDone() does not explicitly return anything.
2) the value that was the result of the logDone() call.

and what the difference if we call a function with the bracket () and without the bracket ()?
If you have () then you call it. If you don't, then you don't.
could you explain to me why i got the result pile up that way?,
In the first example you pass a function to add and then the add function calls it.
In the second example, you call the function and pass its return value (undefined) to add which then mentions it in a statement but doesn't do anything with it.

When you do func([args=optional]), func is getting invocated or called.
and what the difference if we call a function with the bracket () and
without the bracket ()?
We call the function only when we do ()(arguments optional). When a function is without parenthesis, you're just using it's reference.
By storing a reference do a function in a variable, you can call it later, that's what callback is doing.
In the second snippet, since nothing is returned, callback will have undefined. Try calling it by doing callback() in the second snippet, and you should see an error stating,
undefined is not a function.

In the first example, you are passing a function as a parameter.
In your second example, you are passing a functions result after invocation as a parameter.

Related

How does the invocation of this function work?

function functionOne(x){console.log(x);};
function functionTwo(var1) {
};
functionTwo(functionOne(2));
why does functionTwo work there ? it doesn't suppose work, does it?
because there is not an operation.
functionTwo(functionOne(2));
This means "immediately call functionOne, passing in 2. Then pass the result into functionTwo". So functionOne does its thing, logging out 2, and then returns undefined. And then undefined is passed into functionTwo.
Instead, if you're trying to experiment with callbacks you need to pass a function in, as in:
functionTwo(() => functionOne(2));
Once you do that, you'll no longer see a console.log unless you add some code to functionTwo.

Javascript: setTimeout beahavior in this example [duplicate]

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.

Passing javascript functions as parameters

In working with some javascript I have come across behavior which is puzzling to me and I am looking for some documentation which explains what is going on.
The issue seems to be that depending upon how the functions are passed, they might be executed when I would think they are simply parameters.
Here is a simple example:
function f1()
{
alert('f1');
}
function f2()
{
alert('f2');
}
function f3(a, b)
{
alert('f3');
}
Two versions of calling the above:
f3(f1, f2); // shows 1 alert
f3(f1(), f2()); // shows 3 alerts
The first call of f3 above results in the the f1 and f2 functions not being called and you get one alert for "f3". The second call of f3 functions being called, so you get three alerts for "f1", "f2", and "f3".
In actual code I will be using the first version so that f3 can decide if it wants to call f1 and f2. It took me a bit of fiddling to get it right.
I did not expect this behavior and thought that either with or without the parenthesis the f1 and f2 would not be called just by having them be parameters.
Again, I am looking for some documentation that explains how this works.
In your second line:
f3(f1(), f2());
Your are not passing f1 and f2, you are calling them, and passing their (undefined) return values to f3.
Simply put, adding () to a function, even when passing as a parameter will invoke the function immediately. Example of setting a timeout:
function doStuff() {
alert("ok");
}
setTimeout(doStuff(), 300);
You will be alerted ok immediately because the function is invoked immediately. Setting the timeout with:
setTimeout(doStuff, 300);
Will only alert now when the timeout is called, because you are passing the function in correctly as a parameter.
You will commonly see functions passed in as parameters like this in AJAX callback functions.
The documentation for setTimeout has a great section on callback functions: https://developer.mozilla.org/en-US/docs/Web/API/window.setTimeout
(Yes I realize setTimeout was not used in the question, but I felt this example helped answer the question nicely)
You logic is puzzling. You are passing parameters you never user but let's roll
with the punches.
Your question regards the number of alarms;
You are passing parameters to function that never uses them and you invoke it.
so what actually happens is:
function f3(a, b)
{
console.log("argument 1: " + arguments[0]); //Access the arguments fed to the function
console.log("argument 2: " + arguments[1]);
alert('f3');
}
*
f3(f1, f2);
Pops alert "f3"*
and logs:
argument 1: function f1()
{
alert('f1');
}
argument 2: function f2()
{
alert('f2');
}
f3(f1(),f2())
pops all the alerts
and logs:
argument 1 undefined
argument 2 undefined
When a function has parameters its very clear that parenthesis are used to immediately call it
function sum(a, b){
return a + b;
}
console.log( sum(1, 2) ); //prints 3
The f() version is just a special case of this where your parameter list is empty.

setTimeout with or without anonymous function? What's the difference?

I used this code (followed by an xmlhttprequest that fills the "tcap" textarea):
st=setTimeout(checkme(),4000)
where checkme() is:
function checkme() {
if (typeof (st) != 'undefined') clearTimeout(st)
if (document.getElementById("tcap").innerHTML.length > 0) {
document.getElementById('waitmsg').style.display = 'none'
} else {
st = setTimeout(checkme(), 1000)
}
}
If I run it, it freezes Firefox 19 with no error message.
But if I replace the first argument (both in code and in the checkme() function) with:
st=setTimeout(function(){checkme()},4000)
it works correctly.
So my question is: what's the difference in calling the checkme() function with or without the anon function? Why in the first case it freezes Firefox?
Thanks
You need to remove the parens in
st=setTimeout(checkme(),4000)
so instead:
st=setTimeout(checkme,4000)
otherwise, the function is invoked right away.
Since you have the same error inside the checkme function, it probably kills your browser due to unbounded recursion.
setTimeout accepts a function as an argument, and the correct way to pass a function as an argument is either defining it as an anonymous function, or just providing the function name. If you use parenthesis(brackets), you aren't actually passing a function: You are executing the function and passing the result of the function to setTimeout.
Hence, when specifying a function in setTimeout, and anywhere else you need to pass a function as an argument, you should not use parenthesis.
You shouldn't be using the parenthesis within the setTimeout function. You should only be passing in a reference to the method. What you are doing is invoking the method and passing the return value in to the set timeout method.
If you are using setTimeout(checkme(),4000), you are passing the return value of checkme();
But if you want to pass it as a function you need to do in following ways
setTimeout(function(){checkme()},4000)
or
st=setTimeout(checkme,4000)

Difference between 2 jquery binds

function runSomething () {
// some stuff happens
}
$(selector).bind('event', runSomething());
$(selector).bind('event', runSomething);
What's the difference between these 2 versions of the bind?
Here's a practical example:
http://jsbin.com/icajo/edit
Can somebody explain why works the way it does.
I'm trying to get multiple buttons to run the function upon the event, what should I do?
In first case you bind result of runSomething() call, in second - function itself.
update
#JSNewbie, run this and tell what you see in each alert.
function runSomething () {
return 3;
}
var a1 = runSomething();
var a2 = runSomething;
alert(a1);
alert(a2);
In javascript, passing a set of parameters to a function invokes the function, it gets evaluated to the functions return value.
var test = function() { return 1; } // Creates a new function that returns 1
alert(test.toString()); // => "function () { return 1; }"
alert(test().toString()); // => "1"
Even alert itself is just a variable that points to a function.
alert(alert); // => "function alert() { [native code] }"
So, if the first example, when calling runSomething(), it immediately evaluates that function, then passes the return value as a parameter to bind(). In your case, it evals the alert() as the page is loaded, then passes undefined to bind()
In your second example, using the variable runSomething the function itself is passed to bind(). Bind then uses that function only when the event has been raised.
To really blow your mind, you could have function that returns a function, then evaluating the function (like in your first example) is correct... For example
var counter = 0;
function GenerateNext() {
counter++;
return new Function("alert(" + counter + ")");
}
a = GenerateNext();
b = GenerateNext();
b() // will alert 2
a() // will alert 1
$(selector).bind('event', GenerateNext()); // alert 3
$(selector).bind('event', a); // alert 1
$(selector).bind('event', b); // alert 2
It just all depends on what you are trying to do; pass the function itself, or pass the return value of the function.
In the first line the function runSomething is executed within the bind statement, and that what it returns is bound to the event, for example if your runSomething function returns another function then that function is bound, and will be executed upon the event.
On the second line the runSomething function is not executed at that line, and is only only execute when "event" happens.
In javascript functions are treated as variables. Adding the "()" will call the function, and pass the result of the function (which could be 'undefined' if the function returns nothing). The second is the proper way of using the bind method, because it gives a handle to the function to call when the event is triggered.
$(selector).bind('event', 'runSomething()'); (notice the extra quotes around 'runSomething()')
run the function runSomething() when the event is received.
$(selector).bind('event', runSomething);
sets the function runSomething() as the callback function for the event, which means it will receive whatever parameters are included in the event, often this is useful for currentTarget (so you can use the same event on many buttons) or to get specific information from the event (mousemove returns the X,Y location of the mouse as it is triggered).
So if you needed to access the event object that is returned when the event is triggered, the first version wouldn't work properly.
function runSomething(event){
console.log(event); // this would show the event object if the second code is used.
}

Categories

Resources