CALLBACK FUNCTIONS IN JAVASCRIPT
This note explains callback functions in simple language.
You will learn:
- how a function can be used like a value
- what a callback function is
- what a higher-order function is
- what an inline callback is
- how
forEach()works
Callback functions are very important in JavaScript. You will see them in:
- array methods
- events
- timers
- API work
- asynchronous code
- frameworks and libraries
1. Function as a value
In JavaScript, a function is not only something you can call.
A function can also be treated like a normal value.
That means a function can be:
- stored in a variable
- passed as an argument
- returned from another function
This makes functions very powerful.
Diagram 1. A function can behave like a value
Function
│
├─ can be called
├─ can be stored
├─ can be passed
└─ can be returned
Explanation
This is one of the most important ideas in JavaScript.
A function is special, but it can still be used like other values.
2. Calling a function vs using the function itself
Look at this example:
function greet(name) {
return `Welcome ${name}!`;
}
console.log(greet("Aaron")); // "Welcome Aaron!"
console.log(greet); // the function itself
Diagram 2. Parentheses change the meaning
greet("Aaron")
↓
call the function
↓
get the result
greet
↓
do not call the function
↓
use the function itself
Explanation
This is the key difference:
greet("Aaron")means call the functiongreetmeans use the function as a value
3. Why this matters
If we can use a function as a value, then we can pass it into another function.
That is exactly how callback functions work.
Diagram 3. Function passed into another function
one function
↓
passed as an argument
↓
another function receives it
↓
later calls it
Explanation
This is the base idea of callbacks.
4. Callback functions
A callback function is a function that is passed into another function as an argument.
Then the other function uses it later.
Diagram 4. Callback idea
Callback function
↓
passed into another function
↓
called inside that function
Explanation
So a callback is not special because of its syntax.
It is special because of how it is used.
5. First example: greet and notify
function greet(name) {
console.log(`Welcome ${name}!`);
}
function notify(name) {
console.log(`Dear ${name}, your room will be ready in 30 minutes`);
}
These are two normal functions.
They both take a name, but they do different things.
Diagram 5. Two normal functions
greet(name)
→ prints welcome message
notify(name)
→ prints room message
Explanation
Right now, these are just normal functions.
They become callbacks only when we pass them into another function.
6. Higher-order function
Now look at this function:
function registerGuest(name, callback) {
console.log(`Registering ${name}!`);
callback(name);
}
This function takes two parameters:
namecallback
The second parameter is expected to be a function.
Diagram 6. registerGuest structure
registerGuest(name, callback)
│
├─ prints registration message
└─ calls callback(name)
Explanation
This function uses another function passed into it.
That makes it a higher-order function.
7. What is a higher-order function?
A higher-order function is a function that:
- takes another function as an argument
or
- returns a function
In this topic, registerGuest is a higher-order function because it takes a function as a parameter.
Diagram 7. Higher-order function meaning
Higher-order function
│
├─ receives a function
or
└─ returns a function
Explanation
In our case, registerGuest receives a function.
8. Using callback functions
Now let's call registerGuest with real callback functions.
function greet(name) {
console.log(`Welcome ${name}!`);
}
function notify(name) {
console.log(`Dear ${name}, your room will be ready in 30 minutes`);
}
function registerGuest(name, callback) {
console.log(`Registering ${name}!`);
callback(name);
}
registerGuest("Aaron", greet);
// "Registering Aaron!"
// "Welcome Aaron!"
registerGuest("Aaron", notify);
// "Registering Aaron!"
// "Dear Aaron, your room will be ready in 30 minutes"
Diagram 8. First call with greet
registerGuest("Aaron", greet)
Step 1:
name = "Aaron"
Step 2:
callback = greet
Step 3:
print "Registering Aaron!"
Step 4:
callback(name)
↓
greet("Aaron")
↓
print "Welcome Aaron!"
Explanation
greet is passed in as a value.
Inside registerGuest, callback(name) becomes greet(name).
9. Second call with notify
Diagram 9. Second call with notify
registerGuest("Aaron", notify)
Step 1:
name = "Aaron"
Step 2:
callback = notify
Step 3:
print "Registering Aaron!"
Step 4:
callback(name)
↓
notify("Aaron")
↓
print "Dear Aaron, your room will be ready in 30 minutes"
Explanation
This time the same higher-order function works differently, because we passed a different callback.
10. Why callbacks are useful
Callbacks make code more flexible.
The function registerGuest does not need to know exactly what message to show.
It only knows this:
- first register the guest
- then call the callback
So we can change behavior by passing different callback functions.
Diagram 10. Flexible behavior with callbacks
registerGuest(...)
│
├─ with greet → welcome message
└─ with notify → room message
Explanation
This is why callbacks are powerful.
One function can work in different ways depending on the callback.
11. Callback parameter name
This parameter name:
callback
is just a name.
It could be called something else, for example:
actionfnmessageHandler
But callback is a common and clear name.
Diagram 11. Parameter name is flexible
callback
action
fn
All can store a function
Explanation
The important thing is not the name.
The important thing is that the value is a function.
12. Inline callbacks
Sometimes a callback is small and only needed once.
In that case, we can write it directly inside the function call.
This is called an inline callback.
Example
function registerGuest(name, callback) {
console.log(`Registering ${name}!`);
callback(name);
}
registerGuest("Aaron", function greet(name) {
console.log(`Welcome ${name}!`);
});
registerGuest("Benjamin", function notify(name) {
console.log(`Dear ${name}, your room will be ready in 30 minutes`);
});
Diagram 12. Inline callback idea
registerGuest("Aaron", function (...) {
...
})
Callback is written directly inside the call
Explanation
The callback is created exactly where it is needed.
This is useful when the function is small and will not be reused elsewhere.
13. Why inline callbacks are useful
Inline callbacks are useful because:
- they save space
- they keep the logic close to the place where it is used
- they are convenient for short functions
But if the function is large or reused often, it is usually better to declare it separately.
Diagram 13. Separate callback vs inline callback
Separate callback
→ good for reuse
Inline callback
→ good for short one-time use
Explanation
Both styles are valid.
The better choice depends on the situation.
14. The forEach() method
Now let's connect callbacks to something very important: array methods.
forEach() is an array method that uses a callback function.
Syntax
array.forEach(function callback(element, index, array) {
// callback body
});
Diagram 14. forEach() structure
array.forEach(callback)
for each element:
call callback(...)
Explanation
forEach() goes through the array and calls the callback once for every element.
15. What forEach() does
forEach():
- iterates through the array
- calls the callback for each element
- returns
undefined
It is often used instead of for or for...of when you simply want to do something for every element.
Diagram 15. forEach() flow
Array
↓
take first element
↓
call callback
take second element
↓
call callback
take third element
↓
call callback
Explanation
The callback runs again and again for each item in the array.
16. Parameters of the forEach() callback
The callback function in forEach() can receive these parameters:
element- current elementindex- current indexarray- original array
Example form
array.forEach(function (element, index, array) {
// code
});
Diagram 16. Callback parameters in forEach()
element → current value
index → position of current value
array → original array
Explanation
You do not always need all three.
Most often, you only need element.
17. Example: for loop vs forEach()
const numbers = [5, 10, 15, 20, 25];
// Classic for
for (let i = 0; i < numbers.length; i += 1) {
console.log(`Index ${i}, value ${numbers[i]}`);
}
// forEach
numbers.forEach(function (number, index) {
console.log(`Index ${index}, value ${number}`);
});
Diagram 17. for and forEach() do similar work
for loop
→ manually control index
forEach
→ array automatically gives value and index to callback
Explanation
forEach() is shorter and cleaner in many simple cases.
18. Step-by-step forEach() example
const numbers = [5, 10, 15];
numbers.forEach(function (number, index) {
console.log(`Index ${index}, value ${number}`);
});
Diagram 18. What happens in forEach()
Iteration 1:
number = 5
index = 0
Iteration 2:
number = 10
index = 1
Iteration 3:
number = 15
index = 2
Explanation
For each element, forEach() calls the callback again with fresh values.
19. You do not need all callback parameters
If you only need the current element, you can write only one parameter.
Example
const numbers = [5, 10, 15];
numbers.forEach(function (number) {
console.log(number);
});
Diagram 19. Using only needed parameters
Need only value?
→ use element only
Need value and index?
→ use element, index
Need all?
→ use element, index, array
Explanation
This keeps the code simpler.
20. forEach() returns undefined
This is important:
forEach() does not return a useful new array or value.
It returns:
undefined
So it is mainly used for actions like:
- printing
- updating something
- running side effects
Diagram 20. Return value of forEach()
forEach(...)
↓
always returns undefined
Explanation
Even if you write return inside the callback, that does not change the return value of forEach() itself.
21. You cannot stop forEach() early
This is one of the biggest differences between forEach() and loops like for or for...of.
With forEach():
- you cannot use it to stop the whole iteration early in the normal way
It always goes through the whole array.
Diagram 21. forEach() always continues
forEach
↓
goes through all elements
↓
cannot normally stop early
Explanation
If you need early stopping, for or for...of is a better choice.
22. When to use forEach()
Use forEach() when:
- you want to do something for every element
- you do not need to stop early
- you want cleaner array iteration code
Use for or for...of when:
- you need to stop early
- you need more control over the loop
Diagram 22. Choosing between forEach() and loops
Need simple full iteration?
→ forEach()
Need to stop early?
→ for / for...of
Explanation
This is the easiest rule for beginners.
23. Common beginner mistakes
Mistake 1. Calling the callback instead of passing it
Wrong idea:
registerGuest("Aaron", greet());
This calls greet immediately.
Correct:
registerGuest("Aaron", greet);
Here we pass the function itself.
Mistake 2. Forgetting that a callback is just a function value
A callback is not magic.
It is just a function passed as an argument.
Mistake 3. Thinking forEach() returns a new array
It does not.
It returns undefined.
Mistake 4. Thinking forEach() can stop early like break
It cannot do that like a normal loop.
Diagram 23. Common mistakes summary
1. Pass function, do not call it by mistake
2. Callback = function used as an argument
3. forEach() does not return a new array
4. forEach() does not stop early
Explanation
These are very common beginner problems with callbacks.
24. Practical examples
Example 1. Callback with two different behaviors
function sayHello(name) {
console.log(`Hello ${name}`);
}
function sayBye(name) {
console.log(`Goodbye ${name}`);
}
function useName(name, callback) {
callback(name);
}
useName("Nikita", sayHello);
useName("Nikita", sayBye);
Example 2. forEach() with element only
const fruits = ["apple", "banana", "orange"];
fruits.forEach(function (fruit) {
console.log(fruit);
});
Example 3. forEach() with value and index
const fruits = ["apple", "banana", "orange"];
fruits.forEach(function (fruit, index) {
console.log(index, fruit);
});
Diagram 24. Real uses of callbacks
Callbacks are used in:
- custom functions
- array methods
- events
- async code
Explanation
Learning callbacks now helps a lot later.
25. Quick summary
Function as a value
A function can be stored or passed just like other values.
Callback function
A function passed as an argument to another function.
Higher-order function
A function that takes another function or returns one.
Inline callback
A callback written directly inside the function call.
forEach()
An array method that calls a callback for each element.
Diagram 25. Final map of callback functions
Callback functions
│
├─ function as value
├─ callback function
├─ higher-order function
├─ inline callback
└─ forEach(callback)
Explanation
These are the main beginner ideas in this topic.
26. Revision block
1. A function can be used as a value
2. A callback is a function passed into another function
3. A higher-order function takes a function or returns one
4. Inline callbacks are written directly in the function call
5. forEach() uses a callback for each array element
6. forEach() can receive element, index, and array
7. forEach() returns undefined
8. forEach() does not stop early like a normal loop
27. Final conclusion
Callback functions are one of the most important foundations in JavaScript.
If you understand:
- how a function can be used as a value
- what a callback is
- what a higher-order function is
- how inline callbacks work
- how
forEach()uses callbacks
then you already have a strong base for more advanced JavaScript.
Callbacks are used everywhere in full-stack development:
- arrays
- events
- timers
- promises
- APIs
- frameworks
If you want, I can make the next note about arrow functions in the same style with numbered diagrams.
28. Using Callback Functions and forEach() in JavaScript
A callback function is a function that we pass as an argument to another function.
The second function can call the callback when it needs it.
Example
function sayBye(name) {
console.log(`Bye ${name}!`);
}
function doSomething(name, callback) {
callback(name);
}
doSomething("Levi", sayBye);
Output:
Bye Levi!
In this example, sayBye is the callback function.
This line:
doSomething("Levi", sayBye);
means that inside doSomething, JavaScript can call:
callback(name);
and it works like this:
sayBye("Levi");
Part 1: Callback Functions
Exercise 1
Task: Create a function sayBye(name). It should print:
Bye Levi!
Then create this function:
function doSomething(name, callback) {
callback(name);
}
Then call it like this:
doSomething("Levi", sayBye);
Solution:
function sayBye(name) {
console.log(`Bye ${name}!`);
}
function doSomething(name, callback) {
callback(name);
}
doSomething("Levi", sayBye);
Output:
Bye Levi!
Exercise 2
Task: Create a function sayHello(name). It should print:
Hello Levi!
Then create a function doSomething(name, callback). Inside doSomething, call the callback with name.
Then call it like this:
doSomething("Levi", sayHello);
Solution:
function sayHello(name) {
console.log(`Hello ${name}!`);
}
function doSomething(name, callback) {
callback(name);
}
doSomething("Levi", sayHello);
Output:
Hello Levi!
Exercise 3
Task: Now add one line before the callback runs.
Create a function sayHello(name). It should print:
Hello Levi!
Create a function doSomething(name, callback). First it should print:
Starting...
Then it should call the callback. At the end, call:
doSomething("Levi", sayHello);
Solution:
function sayHello(name) {
console.log(`Hello ${name}!`);
}
function doSomething(name, callback) {
console.log("Starting...");
callback(name);
}
doSomething("Levi", sayHello);
Output:
Starting...
Hello Levi!
Exercise 4
Task: Now add one line after the callback.
Create a function sayHello(name). Create a function doSomething(name, callback).
Inside doSomething:
- Print
Starting... - Call the callback
- Print
Finished.
Expected output:
Starting...
Hello Levi!
Finished.
Solution:
function sayHello(name) {
console.log(`Hello ${name}!`);
}
function doSomething(name, callback) {
console.log("Starting...");
callback(name);
console.log("Finished.");
}
doSomething("Levi", sayHello);
Output:
Starting...
Hello Levi!
Finished.
Exercise 5
Task: Create a function sayWelcome(name). It should print:
Welcome Levi!
Create a function doSomething(name, callback). Inside doSomething:
- Print
Starting... - Call the callback
- Print
Finished.
Then call:
doSomething("Levi", sayWelcome);
Solution:
function sayWelcome(name) {
console.log(`Welcome ${name}!`);
}
function doSomething(name, callback) {
console.log("Starting...");
callback(name);
console.log("Finished.");
}
doSomething("Levi", sayWelcome);
Output:
Starting...
Welcome Levi!
Finished.
Exercise 6
Task: Create a function sayGoodMorning(name). It should print:
Good morning Levi!
Create a function doSomething(name, callback). Inside doSomething:
- Print
Starting... - Call the callback
- Print
Finished.
Then call:
doSomething("Levi", sayGoodMorning);
Solution:
function sayGoodMorning(name) {
console.log(`Good morning ${name}!`);
}
function doSomething(name, callback) {
console.log("Starting...");
callback(name);
console.log("Finished.");
}
doSomething("Levi", sayGoodMorning);
Output:
Starting...
Good morning Levi!
Finished.
Exercise 7
Task: This time the callback should use two arguments: name and room.
Create a function showRoom(name, room). It should print:
Levi is in room 204.
Create a function registerGuest(name, room, callback). Inside registerGuest:
- Print
Registering Levi... - Call the callback with
nameandroom
Then call:
registerGuest("Levi", 204, showRoom);
Solution:
function showRoom(name, room) {
console.log(`${name} is in room ${room}.`);
}
function registerGuest(name, room, callback) {
console.log(`Registering ${name}...`);
callback(name, room);
}
registerGuest("Levi", 204, showRoom);
Output:
Registering Levi...
Levi is in room 204.
Exercise 8
Task: Now create two different callback functions.
Create a function showRoom(name, room). It should print:
Levi is in room 204.
Create a function sendMessage(name, room). It should print:
Dear Levi, your room number is 204.
Create a function registerGuest(name, room, callback). Inside registerGuest:
- Print
Registering Levi... - Call the callback with
nameandroom
Then call registerGuest two times:
registerGuest("Levi", 204, showRoom);
registerGuest("Levi", 204, sendMessage);
Solution:
function showRoom(name, room) {
console.log(`${name} is in room ${room}.`);
}
function sendMessage(name, room) {
console.log(`Dear ${name}, your room number is ${room}.`);
}
function registerGuest(name, room, callback) {
console.log(`Registering ${name}...`);
callback(name, room);
}
registerGuest("Levi", 204, showRoom);
registerGuest("Levi", 204, sendMessage);
Output:
Registering Levi...
Levi is in room 204.
Registering Levi...
Dear Levi, your room number is 204.
Exercise 9
Task: Create two callback functions: showPrice(product, price) and showDiscount(product, price).
showPrice should print:
Apple costs 2 euros.
showDiscount should print:
Apple has a discount. New price: 2 euros.
Create a function checkProduct(product, price, callback). Inside checkProduct:
- Print
Checking Apple... - Call the callback with
productandprice
Then call:
checkProduct("Apple", 2, showPrice);
checkProduct("Apple", 2, showDiscount);
Solution:
function showPrice(product, price) {
console.log(`${product} costs ${price} euros.`);
}
function showDiscount(product, price) {
console.log(`${product} has a discount. New price: ${price} euros.`);
}
function checkProduct(product, price, callback) {
console.log(`Checking ${product}...`);
callback(product, price);
}
checkProduct("Apple", 2, showPrice);
checkProduct("Apple", 2, showDiscount);
Output:
Checking Apple...
Apple costs 2 euros.
Checking Apple...
Apple has a discount. New price: 2 euros.
Exercise 10
Task: Create two callback functions: makeUpperCase(word) and makeLowerCase(word).
makeUpperCase should print the word in big letters.
makeLowerCase should print the word in small letters.
Create a function changeWord(word, callback). Inside changeWord:
- Print
Changing Hello... - Call the callback with
word
Use these methods:
word.toUpperCase()
word.toLowerCase()
Then call:
changeWord("Hello", makeUpperCase);
changeWord("Hello", makeLowerCase);
Solution:
function makeUpperCase(word) {
console.log(word.toUpperCase());
}
function makeLowerCase(word) {
console.log(word.toLowerCase());
}
function changeWord(word, callback) {
console.log(`Changing ${word}...`);
callback(word);
}
changeWord("Hello", makeUpperCase);
changeWord("Hello", makeLowerCase);
Output:
Changing Hello...
HELLO
Changing Hello...
hello
Part 2: Inline Callbacks and forEach()
forEach() runs a function one time for every item in an array.
We can use forEach() with a normal function.
Example
const names = ["Levi", "Aaron", "Benjamin"];
names.forEach(function(name) {
console.log(name);
});
Output:
Levi
Aaron
Benjamin
This part is the callback:
function(name) {
console.log(name);
}
It means: take each item from the array and use it as name.
Exercise 11
Task: Create this array:
const fruits = ["Apple", "Banana", "Orange"];
Use forEach() with a normal function to print every fruit.
Expected output:
Apple
Banana
Orange
Solution:
const fruits = ["Apple", "Banana", "Orange"];
fruits.forEach(function(fruit) {
console.log(fruit);
});
Exercise 12
Task: Create this array:
const numbers = [1, 2, 3, 4];
Use forEach() with a normal function to print every number.
Expected output:
1
2
3
4
Solution:
const numbers = [1, 2, 3, 4];
numbers.forEach(function(number) {
console.log(number);
});
Exercise 13
Task: Create this array:
const numbers = [1, 2, 3, 4];
Use forEach() with a normal function to print every number multiplied by 2.
Expected output:
2
4
6
8
Solution:
const numbers = [1, 2, 3, 4];
numbers.forEach(function(number) {
console.log(number * 2);
});
Exercise 14
Task: Create this array:
const numbers = [1, 2, 3, 4];
Use forEach() with a normal function to print this text:
Double of 1 is 2
Double of 2 is 4
Double of 3 is 6
Double of 4 is 8
Use number * 2 to calculate the double.
Solution:
const numbers = [1, 2, 3, 4];
numbers.forEach(function(number) {
console.log(`Double of ${number} is ${number * 2}`);
});
Exercise 15
Task: Create this array:
const numbers = [1, 2, 3, 4];
Use forEach() with a normal function to print every number plus 10.
Expected output:
11
12
13
14
Solution:
const numbers = [1, 2, 3, 4];
numbers.forEach(function(number) {
console.log(number + 10);
});
Exercise 16
Task: Create this array:
const names = ["Levi", "Aaron", "Benjamin"];
Use forEach() with a normal function to print:
Hello Levi!
Hello Aaron!
Hello Benjamin!
Solution:
const names = ["Levi", "Aaron", "Benjamin"];
names.forEach(function(name) {
console.log(`Hello ${name}!`);
});
Exercise 17
Task: Create this array:
const products = ["Milk", "Bread", "Cheese"];
Use forEach() with a normal function to print:
Product: Milk
Product: Bread
Product: Cheese
Solution:
const products = ["Milk", "Bread", "Cheese"];
products.forEach(function(product) {
console.log(`Product: ${product}`);
});
Exercise 18
Task: Create this array:
const prices = [10, 20, 30];
Use forEach() with a normal function to print:
Price: 10 euros
Price: 20 euros
Price: 30 euros
Solution:
const prices = [10, 20, 30];
prices.forEach(function(price) {
console.log(`Price: ${price} euros`);
});
Exercise 19
Task: Create this array:
const prices = [10, 20, 30];
Use forEach() with a normal function to print every price with tax.
Tax is calculated like this:
price * 1.2
Expected output:
Price with tax: 12 euros
Price with tax: 24 euros
Price with tax: 36 euros
Solution:
const prices = [10, 20, 30];
prices.forEach(function(price) {
console.log(`Price with tax: ${price * 1.2} euros`);
});