At this point, I have a question on what exactly is a callback, what makes it different from a high-order function and how does it relate to the concept of callback queue?
From MDN: Callback function
A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
Looks like there's an overlap with the definition of High-Order Functions. A function that gets passed (and later invoked) to another function.
This is the MDN example of a callback:
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
var name = prompt('Please enter your name.');
callback(name);
}
processUserInput(greeting);
No doubt so far.
I then faced with the idea of Event Loop and Callback queue.
console.log("me first");
setTimeout(function asyncLog() {
console.log("i am the last")
}, 2000);
console.log("me second")
Turns out the seTimeout
function, is actually a JavaScript wrapper that interfaces under the hood with Web Browser API (timer). setTimeout passes to the Timer API a function (asyncLog) and the timer (2000ms).
When the timer feature (in the web browser) has completed its work, will send the function asyncLog
in the callback queue
ready to be invoked in the call stack as soon as the JS call stack is (1) Empty and (2) has processed everything in the global execution context.
So after the last line console.log("me second")
is processed the Event Loop passes the callback function asyncLog
from the callback queue to the call stack, and executes it.
The final order is:
me first
me second
i am the last
In the first example, even though we are referring to greeting
as the "callback" function, my understanding suggests that the entire mechanism of Callback queue gets skipped entirely: we're not doing anything asynchronous and we're not interfacing with the web browser API (everything is self-contained into JS).
If so, why do we refer to functions that get passed into other functions as callbacks (and not as simple High-order functions), when they have nothing to do with the callback queue and with the asynchronous world?
Higher-Order Functions(HoF) and Callback Functions(CB) are different. Higher-Order Functions(HoF): A function that takes another function(s) as an argument(s) and/or returns a function as a value. Callback Functions(CB): A function that is passed to another function.
The difference between a callback function and any other function is how it's used. A callback function is specifically built to be used as argument of another function.
A callback queue is a queue of tasks that are executed after the current task. The callback queue is handled by the JavaScript engine after it has executed all tasks in the microtask queue.
A callback function is passed as an argument to another function whereas Promise is something that is achieved or completed in the future. In JavaScript, a promise is an object and we use the promise constructor to initialize a promise.
A higher-order function is a function that takes another function(s) as an argument(s) and/or returns a function to its callers.
A callback function is a function that is passed to another function with the expectation that the other function will call it.
So a callback is not necessarily itself a higher-order function, but a function which receives a callback as an argument is. Consider a very common case, the DOM event listener:
elem.addEventListener('click', console.log);
Here, .addEventListener
is a higher-order function that takes another function (console.log
) which it then calls. Although console.log
is a callback here, it is not in this case acting as a higher-order function itself.
The event loop is a mechanism exposed to you by the underlying runtime. Here we are going to imagine that we have to do this manually, using an array as a queue:
const queue = [];
const setTimeout = (f, ...args) => {
queue.push([f, args]);
};
const runQueuedCallbacks = () => {
let queued;
while (queued = queue.shift()) {
let [f, args] = queued;
f(...args);
}
};
setTimeout(console.log, 'first'); // queues a call to log
setTimeout(console.log, 'second'); // queues a call to log
console.log('zero-th'); // directly calls log, prints first
// at this point there's no more code to execute, so runQueuedCallbacks runs
// and sequentially steps through the enqueued callbacks.
In real life it's a little more complicated than this because of things like microtask resolution (also what happens when a queued callback queues another callback) but that should hopefully give you a decent picture.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With