I'm new to jQuery and am bit confused about the use (or not) of parentheses with a callback function. Say I have a function:
function cb() {
// do something
}
Now what is the difference between:
$("p").hide(1000, cb);
and
$("p").hide(1000, cb());
Is it to do with when the cb function is executed? It would be great if someone could explain this to me in the simplest of terms.
cb() means give me the result of executing the function cb.
cb IS the function cb or, more accurately a pointer (reference) to it.
Is it to do with when the cb function is executed?
Essentially, yes, though the difference does run a little deeper than that.
cb is a reference of sorts to the function. You're passing the function along as a parameter to be invoked somewhere down the line.
cb() is a function call; the function will be invoked, and the result passed as an argument to .hide.
The difference is that in javascript functions are first class objects and can be passed to other functions so that they may executed at a later stage or depending on some logic.
Consider the following:
function add(a, b) {
return a + b;
}
function minus(a, b) {
return a - b;
}
function apply(func, a, b) {
return func(a,b);
}
apply(add, 3, 4); // returns 7
apply(minus, 3, 4); // returns -1
apply(add(), 3, 4); // error: invalid number of arguments for add
apply(add(0,0), 3, 4); // error: add returns 0, but 0 is not a function and
// so apply crashes when it tried to call zero as a function
$("p").hide(1000, cb); passes the function referenced by cb, as a callback.
$("p").hide(1000, cb()); passes the value returned when the function cb is called.
Given:
function cb(){ return true; }
The former is passing the callback for later calling. The latter passes the returned value true, and is essentially $("p").hide(1000, true);
Related
Am a newbie in javascript, please help me understand below case where the callback function code is being considered as a string and passed as argument instead of passing the value of the callback function as an argument:
//Case3: Create Callback function in the argument section of calling statement.
function child3(callback,arg2=3) {
return console.log("Case3: callback function - parent function out:", callback+arg2,"\n");
}
child3(parent3=()=>{
let a=1;
let b=1;
return a+b;
},2);
Output:
Case3: callback function - parent function out: ()=>{
let a=1;
let b=1;
return a+b;
}2
When you use +, there are two possibilities:
If both operands are numbers or BigInts, they are added together
Otherwise, both operands are concatenated together into a string
If you do
callback+arg2
and callback isn't a number, the result will be a concatenation of it and arg2. In your code, callback is not a number; it's a function. You probably wanted to call the callback instead of concatenating it - eg, callback().
Another issue is that
child3(parent3=()=>{
should almost certainly be
child3(()=>{
unless you deliberately wanted to both create a new global function named parent3 and pass that to child3.
function child3(callback, arg2 = 3) {
console.log(callback() + arg2);
}
child3(() => {
let a = 1;
let b = 1;
return a + b;
}, 2);
It's not a string, but a parameter. It has a name in the function's parameter list, which is callback, so, inside the function it is referred to as callback and when you pass
"Case3: callback function - parent function out:", callback+arg2,"\n"
to console.log, you convert the function to string. By the way, you can do that without passing the function as parameter as well
function foo(a, b) {
return a + b;
}
console.log(foo);
So, you do not pass it as a string, you pass it as a function instead, but it is converted into a string, i.e. the function definition when you call console.log.
I need to understand how callback functions works.
I wrote this little script with 2 functions :
function fn1(callback) {
alert('fn1');
callback();
}
function fn2() {
alert('fn2');
}
fn1(fn2); //display 'fn1' then 'fn2'
Now, I want to update my script with a "fn3" function in order to display 'fn1' then 'fn2' then 'fn3'. I tried this :
function fn1(callback) {
alert('fn1');
callback();
}
function fn2(callback) {
alert('fn2');
callback();
}
function fn3() {
alert('fn3');
}
fn1(fn2(fn3));
but it dispay 'fn2', then 'fn3', then 'fn1', then log an error ("callback is not a function").
Any idea ? what's wrong ?
Thanks in advance, Florent.
In order to execute f1 then f2 then f3 you need to create a callback function in order to make sure a function will be executed in steps.
wrong:
fn1(fn2(fn3))) // this first call f2 with f3 as parameter then calls f1 with its result
right:
fn1(function () { // call f1
fn2(function () { // then call f2 after f1
fn3(); // then call f3 after f2
})
})
This sounds more like you're looking for promises
function fn1(){
return new Promise(resolve => {
alert("fn1");
resolve()
});
}
function fn2(){
return new Promise(resolve => {
alert("fn2");
resolve()
});
}
function fn3(){
return new Promise(resolve => {
alert("fn3");
resolve()
});
}
fn1().then(fn2).then(fn3);
fn3
You take the value fn3 which is a function.
fn2(fn3)
You pass that value as the argument to fn2, another function, which you call.
function fn2(callback) { alert('fn2'); callback(); }
You alert, then you call the argument (the function you got from fn3) as a function (I'll skip over the details of what it does) and then return undefined (since you have no return statement).
fn1(fn2(fn3));
Since fn2(fn3) returns undefined this is the same as fn1(undefined).
function fn1(callback) { alert('fn1'); callback(); }
You alert, then try to call undefined as a function, which it isn't, so it errors.
How to call 3 or 4 callback functions
Probably… rewrite the functions so they make use of Promises instead of plain old callbacks.
But there is no point in using callbacks for your example at all, and the best solution to a real problem will depend on what that problem is.
fn1(fn2(fn3))
Here we have a call to function fn1 with 1 argument which is return value of function fn2. This statement is evaluated so that fn2 is first called with its parameter fn3 and then the return value is passed to fn1. fn2 calls its parameter. This is why you get "unexpected" order of alerts.
fn2 does not return anything so that is why you get error from fn1.
Here are two callback function:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p)'
}
If I want use callback_a
function test(callback){
if(condition){
callback();
}
}
test(callback_a);
But the function test isn't applicable to callback_b, So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
There are three options:
The easiest way is to use spread operator:
function test(callback, ...callback_args) {
callback(...callback_args);
}
in this case the invocation of test for function callback_b would be like this:
test(callback_b,"b")
The second way is using arguments which are scoped to any function in JavaScript:
function test(callback) {
callback.apply(null, arguments.slice(1));
}
the invocation of test for function callback_b would be the same:
test(callback_b,"b")
Another options is to use partially applied functions. In this case you should define b_callback like this (ES6 syntax):
let callback_b = (p) => () => void{
alert('b says'+ p)'
}
or without ES6:
function callback_b(p) {
return function(){
alert('b says'+ p)'
}
}
and invoke it like this:
test(callback_b("b"))
There is a special object called arguments that gets created when a function is invoked. It's an array-like object that represents the arguments passed in to a function:
It can be used like this:
test();
// no arguments passed, but it still gets created:
// arguments.length = 0
// arguments >> []
test(a);
// ONE argument passed:
// arguments.length = 1
// arguments >> [a]
test(a,b,c,d);
// FOUR arguments passed:
// arguments.length = 4
// arguments >> [a,b,c,d]
Knowing this, one can call a callback with the rest of the arguments passed in from the parent function using apply like this:
function test(callback) {
callback.apply(null, Array.prototype.slice.call(arguments, 1));
}
// arguments passed into test are available in the function scope when
// .slice is used here to only pass the portion of the arguments
// array relevant to the callback (i.e. any arguments minus the
// first argument which is the callback itself.)
//
// N.B. The arguments object isn't an array but an array like object so
// .slice isn't available on it directly, hence .call was used here)
Might be worth reading up on:
The arguments object
Function.prototype.apply, Function.prototype.call and Function.prototype.bind as they are way to bind a context and arguments to a function (i.e. they'll work with the arguments object to call a function where you may not know how many arguments will be passed)
So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
Basically, you don't. The function receiving the callback is in charge of what the callback receives as arguments. When you call Array#forEach, it's Array#forEach that decides what arguments your callback gets. Similarly, String#replace defines what it will call its callback with.
Your job is to say what test will do, what it will call its callback with. Then it's the job of the person using test to write their callback appropriately. For instance: You might document test as calling the callback with no arguments. If the caller wants to use callback_b, then it's up to them to handle the fact that callback_b expects a parameter. There are several ways they can do that:
The could wrap it in another function:
test(function() {
callback_b("appropriate value here");
});
...or use Function#bind
test(callback_b.bind(null, "appropriate value here"));
...but it's their problem, not yours.
Side note: If they pass you callback_b and you call it without any arguments, you won't get an error. JavaScript allows you to call a function with fewer arguments than it expects, or more. How the function handles that is up to the author of the function.
You can pass an anonymous function as the callback that will itself return your desired callback function with parameters.
test(function() { return callback_b(' how are you'); });
see this working snippet that will first use callback_a, then callback_b (with parameter) as the callback:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p);
}
function test(callback){
if(true){
callback();
}
}
test(callback_a);
test(function() { return callback_b(' how are you'); });
You can pass the parameter while calling the callback
function test(callback){
if(condition){
callback();
}
else if(other condition){
callback("b");
}
}
test(callback_b);
You can write your callback function like
function callback_a_b(){
if(arguments.length){
var arg = [].slice.call(arguments);
alert('b says'+ arg[0])
}
else{
alert('a');
}
}
You can pass array of parameters as second param of test function or in ES6 use spread operator read more here
function test(callback, params){
if(condition){
if (params === undefined){
callback();
} else {
callback.apply(null, params); //params must be array
//ES6: callback(...params);
}
}
}
test(callback_a);
test(callback_b, [" whatever"]);
I've just checked in my browser (ffox 51.0.1) that the following works:
function test(callback,other_args){if(condition){callback(other_args);}}
results:
condition=true
test(callback_a)
=> shows the alert with 'a'
condition=false
test(callback_a)
=> doesn't show anything
condition=true
test(callback_b,"pepe")
=> shows the alert with 'b sayspepe'
condition=false
test(callback_b,"pepe")
=> doesn't show anything
Today, I saw the following code below:
log_execution_time = require('./utils').log_execution_time;
var fib = function fib(n) {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
};
var timed_fib = log_execution_time(fib);
timed_fib(5);
>>> Execution time: 1.166ms
I am curious about function log_execution_time. I don't know how it is.
You can see the input of log_execution_time is a function. How can it call the function with parameter? But all of the methods from w3school need a parameter when calling a function. I assume:
var log_execution_time = function (input_function){
console.time("Execution time");
// input_function
console.timeEnd("Execution time");
}
Thanks and regards
I think the OP is specifically about how the 5 parameter gets passed to the function input_function
Functions are first class objects in JavaScript. You can set identifiers and pass their references around just the same as any other object.
log_execution_time(fib); does not invoke fib, it passes a reference to fib into the log_execution_time function as the first argument. This means the internals can reference fib
timed_fib is a function which can reference the closure from that invocation of log_execution_time due to when it was created, so it can hence invoke the reference to fib as desired
Here is a simple example;
function log(msg) {
console.log(msg);
}
function wrap(fn) {
return function () { // some anonymous function to be our wrapper
console.log('Wrapped:');
fn.apply(this, arguments); // this line invokes `fn` with whatever arguments
// that were passed into the anonymous function
};
}
var foo = wrap(log);
foo('Hello World'); // logs
// Wrapped:
// Hello World
We could also have used the more usual way to invoke fn, for example fn("fizz buzz");, instead of .apply but that would mean we needed to know more about how to invoke fn, which could have been anything
Useful stuff:
Function.prototype.apply
Function.prototype.call
This is known as function currying, in this case the function is being curried with a parameter that also happens to be a function. It may look something like this:
function logTime(f) {
return function() {
var s = new Date();
var r = f.apply(null, arguments);
var e = new Date();
console.log('Time taken ' + (e-s));
return r;
}
}
function numberlogger(n) {
console.log("logged number: " + n)
};
var timedlogger = logTime(numberlogger);
console.log(timedlogger(2));
We call logTime, passing in numberlogger as an argument. Functions in JavaScript are objects and can be passed around like anything else. The logTime function returns a different function that is then stored in timedlogger. When we invoke timedlogger, we're actually invoking the function that logTime returned. That uses a couple of variables to keep track of the start and end times for timing, but uses apply (which every function in js has) to call the original function (numberlogger) whilst passing in any arguments supplied.
I suggest reading up on Functions in Javascript. Here's a nice article from the Mozilla Developer Network (MDN) which is in my opinion, a much better resource than w3schools
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
To answer your question though, functions in javascript are first class citizens, and what that means is that you can think of them as any other object (string,boolean,number etc). They can be saved in variables and they can be passed as arguments into other functions.
In your example, log_execution_time will actually return a function which is essentially a wrapper around the fib function that gets passed to it
The code can be like this:
var log_execution_time = function (input_function){
var f=function(args)
{
var t1=new Date().getTime();
input_function(arguments);
console.warn("Execution time:" +(new Date().getTime()-t1).toString());
}
return f;
}
function abc(arg1,arg2, callback){
console.log(arg1 + arg2) // I am doing something really interesting
if(callback){
callback(arg1+ arg2)
}
}
function add(){
console.log("from call back " + arguments[0])
}
var a =10;
abc(1,2, add)
this works fine, but if we need to send some additional arguments to the call back, what should we do??
Here, Apart from (arg1+ arg2) I need some other arguments to be set from the caller of abc to the callback
And, what is the difference between abc(1,2,add) and abc(1,2,add()) ??
Thanks :)
abc(1,2,add) => Giving the function argument as "function type". This is like giving a pointer to the function for later invoking it.
abc(1,2,add()) => Calling the add() function and giving its return value as argument.
Do you need that the callback support more than an argument? Since JavaScript is a dynamic language, just call the same callback function with more arguments:
callback(1,2,3,4)
callback(1,2,3,4,5)
callback(1,2,3,4,5,6).
JavaScript isn't strict with function signatures: functions have as many arguments as the caller gives to them.
For example, if you've a callback function like this:
function(a, b) {
}
And later you call it this way:
function("hello"); // Where's the second argument??
JavaScript won't complain. Simply b will be undefined.
the difference between add and add():
add is a function and add() is the return value of the function.
if you want to get more arguments of the function. use the arguments object
every function has an arguments object
arguments[0] == 1 arguments[1] == 2 arguments[2] == add.
You can call your callback with parameters from your original function and you can also provide the parameters for the callback inside your function call:
function f1 (args, callback, params) {
callback.apply(this, params); // Params needs to be an array
callback.apply(this, [args]); // This would call callback with 1 param - args
callback.call(this, args); // This is the same as the line above
}
function f2 () {
for (item in arguments) {
console.log(item);
}
}
f1('arg', f2, [1, 2, 3]);
If you call your function with a function call inside the parameters then it would immediately evaluate and it would not be executed as a callback.
The difference between abc(1,2,add) and abc(1,2,add()) is that in the second case, it's not abc that calls add. The consequence is that add executes much sooner than expected and without any arguments.
The usual way to pass extra parameters to a callback is to create an anonymous function that closes over the parameters:
var a=10;
var b=20;
abc(a, b, function(text){
add(a, b);
console.log(text); //logs "abc"
});