A higher-order function is a function that takes another function as an argument, returns a function, or both. A callback is a function passed into another function to be invoked later.
Functions are first-class values in JavaScript — they can be stored in variables, passed as arguments, and returned. Higher-order functions leverage this: array methods (map, filter, forEach) take callbacks; event listeners and setTimeout take callbacks; and functions like a custom retry() or once() return new functions. Callbacks can be synchronous (called immediately, like in map) or asynchronous (called later, like in setTimeout or I/O). Higher-order functions enable powerful patterns — composition, currying, memoization, and middleware. The downside of nested async callbacks is 'callback hell', which promises and async/await were created to solve.
A reusable retry wrapper for flaky test steps: function withRetry(fn, times) { return async (...args) => { for (let i = 0; i < times; i++) { try { return await fn(...args); } catch (e) { if (i === times - 1) throw e; } } }; } — a higher-order function that returns a hardened version of any async step.
Write a higher-order function once(fn) that returns a function which invokes fn only the first time it is called and returns the cached result afterward.