Why callbacks exist
Some functions know how to do something but not what. A loop knows how to repeat but not what to repeat each lap. An array method knows how to visit every item but not what to do with each one.
The missing piece arrives as an argument. A function passed into another function so it can be called there is a callback, since the receiver calls it back at the right moment. This is the shape of most JavaScript APIs: unit 7's map and filter, browser event handlers that run when something is clicked, and timers all take callbacks.
function repeat(times, action) { for (let i = 1; i <= times; i++) { action(i); } } repeat(3, n => console.log(`Lap ${n}`));
Inside repeat, the parameter action is an ordinary variable that happens to hold a function, exactly as lesson 6-2 described, so action(i) calls it. The arrow you pass decides what each lap does, and repeat never needs to know what that is.
Splitting the loop from the work
repeat owns the loop and the arrow owns the body. Neither half knows the other's details, which is what makes repeat reusable.
function repeat(times, action) { for (let i = 1; i <= times; i++) { action(i); } } repeat(3, n => console.log(`Lap ${n}`));
Output
Lap 1 Lap 2 Lap 3
The counter i is passed into the callback as action(i), and the arrow receives it under the name n. Those two names refer to the same value on each lap, and they differ only because each side chose its own label, which is normal and worth getting comfortable with.
Everything about the output is controlled from the call site. The 3 decides how many laps run, so passing 5 would produce five lines, and the arrow decides what each line says, so an arrow of n => console.log(n * 10) would print 10, 20, and 30 instead. The body of repeat would not change in either case.
Pass the function, do not call it
The classic callback bug comes down to one pair of parentheses:
repeat(3, sayHi()); // WRONG: calls sayHi NOW, passes its return value repeat(3, sayHi); // right: passes the function itself
Parentheses mean run this now. So sayHi() executes immediately and hands repeat whatever it returned, usually undefined, and the program crashes later when repeat tries to call a value that is not a function. The bare name sayHi hands over the function itself, ready to be called whenever the receiver decides.
Named functions, function expressions, and arrows all work equally well as callbacks. Inline arrows are the everyday choice when the behavior is short enough to read at the call site, and a named function is better when the behavior deserves a name or gets reused.
One function, three operations
calculate does not know any arithmetic. It takes two values and a function, and the function it receives decides what happens to them, which is the whole callback idea in four lines.
function calculate(a, b, operation) { return operation(a, b); } const add = (x, y) => x + y; const multiply = (x, y) => x * y; console.log(calculate(4, 5, add)); console.log(calculate(4, 5, multiply)); console.log(calculate(10, 3, (x, y) => x - y));
Output
9 20 7
The first two calls pass named arrows, and notice they are passed as add and multiply with no parentheses, since adding them would call the functions right there and hand over their results instead.
The third call skips the variable entirely and writes the subtraction arrow directly in the argument list. That inline style is what unit 7 uses almost exclusively, because a one-off transformation rarely deserves a name of its own.
Passing a function, not calling it
Given function twice(fn) { fn(); fn(); } and a function greet, the correct call is twice(greet).
Writing twice(greet()) runs greet immediately and passes its return value, undefined, into twice, which then crashes on fn() because undefined is not callable. The bare name greet hands over the function object itself, so twice can be the one to call it, twice.
The parentheses are the whole distinction: greet is the function, greet() is the result of running it.