Please help me understand two thing about callbacks. They are the last JS weirdness left for me that I can't grasp having strong C# background.
First - why should we send function as parameters. I know that we can, but why. I will try the simplest example I can think of. So according to the JS developers it should be like this:
function simpleCallbackFn(){}
function simpleCallerFn(cb){ cb();}
simpleCallerFn(simpleCallbackFn);
Why not like this:
function simpleCallbackFn(){} // Same as before
function simpleCallerFn(){
simpleCallbackFn();
}
We do not pass a callback, we simply call it from within the caller. It is within perfectly visible scope. Then we just make the initial call.
simpleCallerFn();
Calling the "callback" function from within the caller function to me should be perfectly fine and achieve the same result.
Second - say I need to use callback with arguments
function simpleCallbackFn(anArgument){}
function simpleCallerFn(cb){
cb(anArgument);
}
We know that this will not work
simpleCallerFn (simpleCallbackFn(anArgument));
According to the JS developers it should be like this:
simpleCallerFn (() => simpleCallbackFn(anArgument));
I am thinking why not:
function simpleCallbackFn(anArgument){}
function simpleCallerFn(anArgument, cb){
cb(anArgument);
}
Then make the initial call like this:
simpleCallerFn(anArgument, simpleCallbackFn);
Then combining my first and second approach:
function simpleCallbackFn(anArgument){}
function simpleCallerFn(anArgument){
simpleCallbackFn(anArgument);
}
simpleCallerFn(anArgument);
Please. That Javascript makes me fell like in freak's circus.
Thank you.
Why not like this:
function simpleCallbackFn (){} // Same as before function simpleCallerFn (){ simpleCallbackFn ();}
That's certainly legal code, but it's only possible if you know ahead of time exactly what code to execute. The point of a callback function is that you can decide what you want to do at runtime.
For example, there's a built in function that arrays have called .map
. It takes an array and converts it to a new array. You pass in a callback function specifying how to do that conversion, and .map
will call your function for each element of the array. There's no other way .map
could be implemented, because there's no way to know ahead of time what conversion you want to do.
const values = [1, 2, 3];
const doubled = values.map(val => val * 2);
// doubled contains [2, 4, 6]
btw, a simplified implementation of map would look something like this:
function map(callback) {
const newArray = [];
// where `this` is the input array -- values in my example
for (let i = 0; i < this.length; i++) {
newArray[i] = callback(this[i]);
}
return newArray;
}
We know that this will not work simpleCallerFn (simpleCallbackFn(anArgument));
That's correct. And just to state the reason why it doesn't work: the order of operations for this line moves from the inside out. Basically, it means "immediately call simpleCallbackFn(anArgument)
, and then pass the result into simpleCallerFn
". So we end up passing in undefined
instead of a callback function.
On the other hand, simpleCallerFn(() => simpleCallbackFn(anArgument));
means "Create a function with the text () => simpleCallbackFn(anArgument)
and pass it into simpleCallerFn
".
I am thinking why not:
function simpleCallbackFn (anArgument){} function simpleCallerFn (anArgument, cb){ cb(anArgument); }
That works if you're certain that the callback wants exactly one argument to be passed in to it. But it won't work if they need 2 or more, and if they need 0 it will be more combersome than necessary.
So the benefit of having the user of this code create a callback function is that they can make any function, with any requirements they can dream up.