Back to: JavaScript Tutorial For Beginners and Professionals
JavaScript Callback functions with Synchronous and Asynchronous
In this article, I am going to discuss JavaScript Callback functions with Asynchronous and synchronous with Examples. Please read our previous article where we discussed JavaScript function are the First-class citizen. As part of this article, we are going to discuss the following pointers which are related to JavaScript Callback functions with Asynchronous and synchronous.
- Why do we need callback Functions?
- What is callback?
- When to use callback functions in JavaScript?
- JavaScript Synchronous Callback Functions
- JavaScript Asynchronous Callback Functions
- JavaScript Callback Functions with this keyword
- JavaScript Callback as an Anonymus function
- JavaScript Callback as an arrow function
- Callback as event declaration
- Handling Errors in JavaScript Callback Functions
- How to avoid Callback Hell/ Pyramid of doom
- Javascript Callback vs Closure
Why do we need callback Functions?
As we know that JavaScript is a single-threaded scripting language. Most of the time, JavaScript code runs synchronously. That means that first the first line of code is executed, then the next line code is executed, and so on.
As we learned that JavaScript is client-side scripting so when client-side JavaScript runs in the web browser, and the main browser process is a single-threaded event loop. If we try to run long-running operations within a single-threaded event loop, the process is blocked. This is technically poor because the process stops processing other events while waiting for your operation to complete.
Everything is as we expect, and how it works in most programming languages. However, there are times when we cannot just wait for a line of code to execute. We can’t just wait 2 seconds for a big file to load and halt the program completely. We can’t just wait for a network resource to be downloaded, before doing something else. JavaScript solves this problem using callbacks.
For example, the “alert” statement which shows the alert box is found as one of the blocking codes in JavaScript in the browser. If we run an alert statement it will show the alert box, then we can no longer do any interaction/operation within the browser until we close the alert dialog window. To prevent blocking on long-running operations, a callback comes into this picture and is used.
Let see some example to understand it better
<html> <head> <title>JavaScript example without callback</title> </head> <body> <script> function getMessage() { console.log("get message"); } function showMessage() { console.log("show message"); } getMessage(); showMessage(); </script> </body> </html>
Output:
In the above example, getMessage() function is executed first and then displayMessage() is executed. Both functions showed a message in the browser’s console window and both of them executed immediately one after the other.
Suppose there is a situation where some code is not executed immediately. We might be thinking about how? So let take an example of the above code and consider that the getMessagae() perform some operations such as API calls where we have to send the request to a server and wait for a response. In such condition how we will be able to deal with it? So, this is where the callback concept comes in.
What is callback?
As per MDN: 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.
Callback functions can be synchronous or asynchronous. The callback function as the name implies is the function that gets executed after the other function has finished executing. A callback is a function that is passed as a parameter into another function to be executed later to perform some operation. The callback function is executed asynchronously.
When to use callback functions in JavaScript?
The callback function is used in several tasks such as
- when working with the file system (downloading or uploading),
- Sending the network request to get some resources such as test or binary file from the server,
- events,
- the DOM in the browser
- or working with web APIs to fetch data.
To handle the above situation, we must use a write asynchronous code using a callback function. As we know that the callback function is asynchronous in nature.
One of the simplest examples of how to use callbacks is timers. Timers are not part of JavaScript, but they are provided by the browser. Let me talk about one of the timers we have: setTimeout().
The setTimeout() function accepts 2 arguments: a function, and a number. The number is the milliseconds that must pass before the function is run. setTimeout() is a JavaScript asynchronous function that executes a code block or evaluates an expression through a callback function after a delay set in milliseconds.
Example:
<html> <head> <title>JavaScript callback with setTimeout example</title> </head> <body> <script> console.log("hi") setTimeout(() => { // runs after 3 seconds console.log('Hello callback setTimeout function') }, 3000) console.log("bye") </script> </body> </html>
Output:
In the above example function containing the console.log(‘Hello callback setTimeout’) line will be executed after 3 seconds. On adding the console.log(“hi”) and console.log(“bye”) we can see that what is happening.
JavaScript Callback Synchronous
The callback can be used to execute the code once the method execution is finished.
Example-1:
<html> <head> <title>JavaScript Callback synchronous example1</title> </head> <body> <script> function doSomething(then) { console.log('call first'); then(); } // call first, then execute callback to log 'done' doSomething(function () { console.log('Done'); }); console.log('call second'); </script> </body> </html>
Output:
In the above synchronous example, the doSomething() method above executes synchronously with the callback – execution blocks until doSomething() returns, ensuring that the callback is executed before the interpreter moves on.
Example-2
<html> <head> <title>JavaScript Callback synchronously example2</title> </head> <body> <script> function greeting(name) { alert('Hello ' + name); } function takeUserInput(callback) { var name = prompt('Please enter your name.'); callback(name); } takeUserInput(greeting); </script> </body> </html>
Output:
In the above example, the takeUserInput(greeting) function executes the code synchronously with a callback function.
JavaScript Callback Asynchronous
Callbacks can also be used to execute code asynchronously. An Example is given below.
<html> <head> <title>JavaScript Callback synchronous example</title> </head> <body> <script> function doSomethingAsync(then) { setTimeout(then, 1000); console.log('call first asynchronously'); } doSomethingAsync(function () { console.log('Done'); }); console.log('call second'); </script> </body> </html>
Output:
In the above asynchronous example, the then callbacks are considered continuations of the doSomething() methods. Providing a callback as the last instruction in a function is called a tail-call, which is optimized by ES2015 interpreters.
The callbacks function is commonly used to continue execution of the code even after an asynchronous action has completed, these are called asynchronous callbacks. An example is the callback function executes inside a, then block chained into the end of a promise after that promise fulfills or rejects.
JavaScript Callback Functions with this keyword
Often when using a callback we want access to a specific context.
Example: callback with this keyword
<html> <head> <title>JavaScript Callback asynchronously with this keyword example</title> </head> <body> <script> function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click', function () { console.log(this.msg); // <= will fail because "this" is undefined }); } var s = new SomeClass("hello", someElement); </script> </body> </html>
In the above example, the line console.log(this.msg) won’t print it with this keyword as this is undefined. We can do this by using the bind() function. The bind effectively generates a new function that sets this to whatever was passed to bind then calls the original function.
Example: callback with this and bind function
<html> <head> <title>JavaScript Callback asynchronously with this and bind() example</title> </head> <body> <script> function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click', function () { console.log(this.msg); }.bind(this)); // <=- bind the function to `this` } </script> </body> </html>
JavaScript Callback as an Anonymus function:
We can define a callback function directly inside another function, instead of calling it. It will be like this:
Example:
<html> <head> <title>JavaScript Callback asynchronously as an anonymous function example</title> </head> <body> <script> setTimeout(function () { console.log("This message is shown after 3 seconds"); }, 3000); </script> </body> </html>
Output:
As we can see in the above example, the callback function here has no name and a function definition without a name in JavaScript is called an “anonymous function”. This does exactly the same task as the example below.
JavaScript Callback as an arrow function:
Using callback function as an ES6 arrow function can reduce lines of code. About arrow function will see this more in detail in the Arrow function chapter. Let check the behavior of callback using an arrow function.
The syntax for arrow function is () => {}
This can be used as callbacks. If we prefer, we can also write the same callback function as an ES6 arrow function, which is a newer type of function in JavaScript:
<html> <head> <title>JavaScript Callback asynchronously as an arrow function-basic example</title> </head> <body> <script> setTimeout(() => { console.log("This message is shown after 3 seconds"); }, 3000); </script> </body> </html>
Output:
Let take another example if we want to print all elements in an array [1,2,3,4,5]
Without the arrow function, the code will look like this
<html> <head> <title>JavaScript Callback asynchronously without arrow function example</title> </head> <body> <script> [1, 2, 3, 4, 5].forEach(function (x) { console.log(x); }) </script> </body> </html>
Output:
With arrow function, it can be reduced to
<html> <head> <title>JavaScript Callback asynchronously with arrow function example</title> </head> <body> <script> [1, 2, 3, 4, 5].forEach(x => console.log(x)); </script> </body> </html>
Output:
In the above with arrow example, the callback function function(x){console.log(x)} is reduced to x=>console.log(x)
Callback as event declaration
As we know that JavaScript is an event-driven programming language. We can also use callback functions for declaring an event. For example, suppose we want the user to click on a button:
Example:
<html> <head> <title>JavaScript Callback function as event declaration example</title> </head> <body> <button id="callback-btn">Click here</button> <script> document.querySelector("#callback-btn") .addEventListener("click", function () { console.log("User has clicked on the button!"); }); </script> </body> </html>
Output:
In the above example, we have selected the button with its id, and then we have added an event listener with the addEventListener method. It takes 2 parameters. The first parameter is its type, “click”, and the second parameter is a callback function, which displays the message in the browser console when the button is clicked. As we can see, callback functions are also used for event declarations in JavaScript
Handling Errors in JavaScript Callback Functions
Callbacks are commonly used to provide error handling. This is a format of control flow, where some instructions are executed only when an error occurs. The following code introduces two callbacks: success and failure to handle the success and failure cases respectively.
Example: JavaScript Callback-Error handling synchronous
<html> <head> <title>JavaScript Callback-Error handling synchronous example</title> </head> <body> <script> const expected = true; function compareData(actual, success, failure) { if (actual === expected) { success(); } else { failure(); } } function onSuccess() { console.log('Actual Value is same as expected Value'); } function onFailure() { console.log('Actual Value is unexpected/exceptional'); } compareData(true, onSuccess, onFailure); compareData(false, onSuccess, onFailure); </script> </body> </html>
Output:
In the above example, code execution in compareData() above has two possible branches: success when the expected values and actual values are the same, and error when they are different. This is useful when the control flow should split after some asynchronous operation.
Example: JavaScript Callback-Error handling Asynchronous
<html> <head> <title>JavaScript Callback-Error handling Asynchronous example</title> </head> <body> <script> const expected = true; function compareData(actual, success, failure) { if (actual === expected) { success(); } else { failure(); } } function onSuccess() { console.log('Success: Actual Value is same as expected Value'); } function onFailure() { console.log('Failure: Actual Value is unexpected/exceptional'); } function compareDataAsync(actual, success, failure) { setTimeout(function () { compareData(actual, success, failure) }, 1000); } compareDataAsync(true, onSuccess, onFailure); compareDataAsync(false, onSuccess, onFailure); console.log('Doing something else'); </script> </body> </html>
Output:
Let’s take another example of downloading the file. The fileDownload() function assumes that everything works fine and does not consider any exceptions. The following code introduces two callbacks: success and failure to handle the success and failure cases respectively.
Example: JavaScript Callback-Error handling file Download Asynchronous
<html> <head> <title>JavaScript Callback-Error handling file Download Asynchronous example</title> </head> <body> <script> function fileDownload(url, success, failure) { setTimeout(() => { // script to download the picture here console.log(`Downloading ${url} ...`); // over simplification let error = url.length === 0 || !url; // call the failure or success callback error ? failure(url) : success(url); }, 3000); } fileDownload('', function (picture) { console.log(`Processing the picture ${picture}`); }, function (picture) { console.log(`Handling error...`); } ); fileDownload('https://unsplash.com/s/photos/jpg', function (picture) { console.log(`Processing the picture ${picture}`); }, function (picture) { console.log(`Handling error...`); } ); </script> </body> </html>
Output:
JavaScript Callback Hell (the Pyramid of Doom) and the Nesting callbacks
Nesting many asynchronous functions inside callbacks is known as the pyramid of doom or the callback hell:
Example: Nesting callbacks and callback hell
asyncFunction(function () { asyncFunction(function () { asyncFunction(function () { asyncFunction(function () { asyncFunction(function () { //.... }); }); }); }); });
Callback hell occurs when there are multiple asynchronous functions are executed one after another. It is also known as the pyramid of doom.
Considering the previous fileDownload() program example.
Suppose the scenario where we need to download multiple images continuously. How do we download multiple pictures and process them sequentially? A typical approach is to call the fileDownload() function inside the callback function, like this: To implement the same functionality with the help of callbacks, the code snippet will look like this.
Example: JavaScript Callback Hell or the Pyramid of Doom
<html> <head> <title> JavaScript Callback Hell or the Pyramid of Doom example</title> </head> <body> <script> function fileDownload(url, callback) { setTimeout(() => { // script to download the picture here console.log(`Downloading ${url} ...`); // process the picture once it is completed callback(url); }, 3000); } const url1 = 'https://www.freepik.com/free-photos-vectors/jpg/pic1.jpg'; const url2 = 'https://www.freepik.com/free-photos-vectors/jpg/pic2.jpg'; const url3 = 'https://www.freepik.com/free-photos-vectors/jpg/pic3.jpg'; const url4 = 'https://www.freepik.com/free-photos-vectors/jpg/pic4.jpg'; fileDownload(url1, function (picture) { console.log(`Processing ${picture}`); // download the second picture fileDownload(url2, function (picture) { console.log(`Processing ${picture}`); // download the third picture fileDownload(url3, function (picture) { console.log(`Processing ${picture}`); }); // download the fourth picture fileDownload(url4, function (picture) { console.log(`Processing ${picture}`); }); }); }); </script> </body> </html>
Output:
The above example code works fine. However, this callback function does not scale well when the complexity grows. From the above code snippet, we can see that the code becomes harder to understand, harder to maintain, and also harder to modify. This happens with the nesting of all the callback functions.
How to avoid Callback Hell/ Pyramid of doom
to avoid callback hell or the pyramid of doom we can use multiple techniques which are as follows:
- By using promises
- By using async/await functions.
Javascript Callback vs Closure
Closure
- In JavaScript, closure is an expression that is assigned to a variable, can be passed as an argument to a function, or be returned as a function result.
- Closures allow us to access to an outer function’s scope from an inner function
- Closure refers to how a function closes over its lexical scope. Hence, the function had closure.
Callback
- The callback is a similar concept to closure. A callback function is a function passed as a parameter to another function to execute later. While during code execution, the called function will execute the function that is passed as an argument, this is called a callback. It is called inside the other function.
- One major use case of this the performance of asynchronous operations by putting a function into the runtime event queue.
- A callback is a function that in the end gets called back to the calling scope. Hence, it’s called a callback.
In the next article, I am going to discuss JavaScrpt Anonymous Functions with Examples. Here, in this article, I try to explain the JavaScript Callback function with Asynchronous and synchronous with examples. I hope this JavaScript Callback function with an Asynchronous and synchronous article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this JavaScript Callback function with Asynchronous and synchronous.
Thank you for articles. Would like to see some examples of making callbacks to the controller to handle some tasks server side and return whatever results. Is this possible?