A callback is a function that is passed into another function as an argument which can be invoked later inside the function.
Synchronous callbacks
Synchronous means the code statements execute immediately after one another in a sequential manner.
function print(number, result) { console.log(`${number} is ${result}`); } function checkEvenOrOdd(number, callback) { const result = (number % 2 === 0) ? 'Even' : 'Odd'; callback(number, result); } checkEvenOrOdd(56, print); // 56 is Even
Here the callback is executing immediately and it is not waiting for any asynchronous operation to finish. That’s why it is a synchronous callback.
Asynchronous callbacks
If a callback is executing after an asynchronous operation has finished then it is an asynchronous callback.
Let’s see one example where we will take an order and print it.
function takeOrder() { setTimeout(() => { return (Math.random() * 10) <= 5 ? 'Coffee' : 'Tea'; }, 1000); } let order = takeOrder(); console.log('Order is for: ' + order); // Order is for: undefined
Here in the takeOrder
function, the setTimeout
will run after 1 sec and by that time the console.log
statement has already executed therefore printed value of order
as undefined.
Now we can resolve this issue if we can log our message to the console only after the data has returned from takeOrder
. This can be done by passing a callback function to takeOrder
which will be invoked inside takeOrder
function.
function takeOrder(callback) { setTimeout(() => { const order = (Math.random() * 10) <= 5 ? 'Coffee' : 'Tea'; callback(order); }, 1000); } takeOrder((order) => { console.log('Order is for: ' + order); }); // Order is for: Tea
Here, after 1 second, the callback function will be called and the console statement will get executed with the correct order value.
The output of
takeOrder
function may differ in your case as we are usingMath.random()
to decide order value.
Handling errors with callbacks
We can pass different callbacks for both success and failure scenarios.
function takeOrder(success, failure) { setTimeout(() => { const random = (Math.random() * 10); if(random < 8) { const order = random < 4 ? 'Coffee' : 'Tea'; success(order); } else { failure('Order Not Available'); } }, 1000); } takeOrder( (order) => { console.log('Order is for: ' + order); }, (error) => { console.log(error); } );
Nested callbacks
Let’s see the order process one by one.
function takeOrder(callback) { setTimeout(() => { const order = (Math.random() * 10) <= 5 ? 'Coffee' : 'Tea'; callback(order); }, 1000); } function makeOrder(order, callback) { setTimeout(() => { callback(order + ' is prepared'); }, 1000); } function serveOrder(order, callback) { setTimeout(() => { callback(order + ' is served'); }, 1000); } takeOrder((order) => { console.log('Order is for: ' + order); makeOrder(order, (orderStatus) => { console.log(orderStatus); serveOrder(order, (orderStatus) => { console.log(orderStatus); }) }) });
Output
Order is for: Coffee Coffee is prepared Coffee is served
Here makeOrder
is called when the response from takeOrder comes. Similarly serveOrder is called when the response from makeOrder
comes. Here we are nesting callbacks inside one another to execute functions in a sequence.
If the nesting of callbacks increases then it is called a callback hell where it is difficult to manage the callbacks and it reduces the readability of the code. Take a look at a sample callback hell.
takeOrder((order) => { makeOrder(order, () => { serveOrder(order, () => { provideBill(order, () => { takeBill(order, () => { // some more callbacks }) }) }) }) });
This callback hell can be fixed by using promise and async/await.
There are some built-in methods available in JavaScript that accept callbacks as arguments.
// Array.map() array.map((element) => { // your code here }); // setTimeout setTimeout(() => { // your code here }, timeout);
Let’s see the other methods that accept callbacks.
Array.filter()
, Array.reduce()
, Array.reduceRight()
, Array.every()
, Array.some()
, Array.find()
, Array.findIndex()
, Array.sort()
, Array.forEach()
, setInterval()
, addEventListener()
, Promise.then()
, Promise.catch()
etc.
You may also like
- A brief guide to Promises in JavaScript
- Understanding async and await in JavaScript
- 20 JavaScript Shorthand Techniques that will save your time
- JavaScript Array.forEach() method to loop through an Array
- Array.reduce() method in JavaScript
Web Developer by Profession, Blogger by Passion.
JavaScript | React | Next | Angular | Vue | HTML | CSS
One of the best explanations of callback functions and asynchronous tasks in JS!
Thank you!
thanks buddy
Thanks for thr great article!