Callback Function:
What are callback functions?
Just as the names implies, it is a function that is called back whenever it is needed. In JavaScript, callback functions are passed into another function as a param with the sole aim of the callback to run only when the parent function is called or ran.
This may be due to the fact that the callback function depends on the parent function to give it the params required or the parent function must run before the callback function runs. This is widely used in asynchronous programming as the result from a particular run must be `piped` as a parameter to the next function (callback function).
Let’s take a few examples in JS for clarity.
To understand callbacks, we need to understand what
function declaration (giving it behavior) vs function Invocation (making it execute) mean.
//This is a functon declaration or difintion in js.
function declareFunction(){
console.log('I will only run when called.')
}
// or using arrow function syntax to declare a function
const declareFunction = () => {
console.log('I will only run when called')
}
//This is a functon invocation in js, I invoke a function call.
declareFunction() // I will only run when called
/**
* Js functions are first-class functions.
* meaning, you can take the the function name and pass it
* as a parameter to another function
* and run it in that function.
* This is the basis of all callbacks.
*/
Example 1 on callbacks
In this example, func1 has params that should be initialized when func2 is called. For this to be possible we need to call func1 only when func2 is called.
Remember functions in JavaScript are first-class functions so we can pass function declarations as params to another function.
function func1 (name, age, surname){
if(name === undefined || age === undefined){
throw new Error('I need name and age to run')
}
console.log(`My name is ${name} ${surname} and I'm ${age} years old, thank you!`)
}
function func2(func1){ // passing func1 as a callback here
const name = 'Bolu'
const surname 'Oguntade'
const age = 10
func1(name, surname, age)
console.log('This was called from func2')
}
func2()
// My name is Bolu Oguntade and I'm 10 years old, thank you!
// This was called from func2
Example 2 on callbacks
Assuming we want a function to read a variable only if there are no errors or exception while reading, we can do that with these conditions in mind:
Condition 1: When err param is null, the printed value is myTempDoc
Condition 2: When err param is not null, the error is printed on the console.
Note: setTimeout function is a browser API that uses callback for asynchronous operations as well.
Example 3 on callbacks
Last Example on callbacks. When a file is read into memory in nodejs using fs, a callback function is passed into the readFile
async function as the third param to make sure that errors are handled if the reading process fails. This is similar to the previous instance we stated.
Higher Order Function (HOF):
Remember those functions that received a callback as a param? Yes, those are higher order functions in JS.
Inbuilt functions such as map, filter, reduce etc. are HOFs because they expect a callback function that it will execute at runtime.
const numbers = [1, 2, 3, 4];
const squares = numbers.map(num => num * num);
console.log(squares); // Output: [1, 4, 9, 16]
Closures
Closures are close over variable environments
Simply put, a closure can be a function that has access to its own scope, the scope of its outer function, and the global scope, even after the outer function has finished executing. Closures allow functions to "remember" and access variables from their outer scope even when the function is executed outside that scope. A closure wouldn’t necessarily return a direct inner function.
This is a weird terminology in JavaScript but let’s walk through it together.
Example 1: Returns an inner function
// This is our example function to show case closures in js
const multiplierFunction(multiplier){
return function(number){ // Let's call this inner function
return number * multiplier
}
}
/**
* Inner function has a closure property that contains variables outside it's scope
* Let's say an object Closure = {multiplier: // whatever value of multiplier is passed}
* Whenever the inner function is called, it can always remember this value even though the
* function that originally holds the value has ended its execution.
*/
const multiplyByTwo = multiplierFunction(2) // This is the inner function declaration returned
// and not the result and neither is it a variable
// nor constant.
const multiplyByThree = multiplierFunction(3) // remember this inner functions need a param of number
// when called
console.log(multiplyByTwo(3)) // prints 6 in the console
console.log(multiplyByThree(3)) // prints 9 in the console
In the above example, Inner function has access to its scope, the parent’s scope and the global scope even after the parent’s function has finished executing.
THIS IS CLOSURE.
Example 2: Does not returns a direct inner function
// Let's create a counter without returning a function
function closureCounter(){
let count = 0
return {
increment: function(){ count++; return count },
decrement: function(){ count--; return count },
increment: function(){ return count }
}
}
const counter = createCounter();
console.log(counter.increment()); // Output: 1
console.log(counter.increment()); // Output: 2
console.log(counter.decrement()); // Output: 1
console.log(counter.getCount()); // Output: 1
I hope with these brief explanations and code snippets, callbacks, HOFs and closures are now clear concepts.
EXERCISE
// Exercise
// Try to explain this in the comment
function fOuter() {
const m = 50;
return function fInner() {
const n = 51;
console.log(n + m);
};
}
fOuter(); // will this return 101 ?