Making JavaScript Asynchronous

Photo by Clément Hélardot on Unsplash

JavaScript is a multifaceted language. It can be client side, server side, object oriented and most importantly it is synchronous.

Synchronous languages as the name suggests, executes the code line by line, each waiting for the previous line to finish with execution. Synchronous execution is great when there is data to be processed that depends on or is depended upon on some other data. However not all situations are the same. In case of independent data processing, the code would remain stuck executing that one line and waste useful resources.

For example, consider this scenario :

When a user needs to carry out a task that accesses or saves data to a database continuously, the user might have to wait for it to execute one at time whilst seeing only a “loading” message on the screen. Instead the task can go on in the background while the user can engage in other useful tasks. This is known as Asynchronous Programming. JavaScript can be made asynchronous using three ways: Callbacks, promises and async/await.

Callbacks

Callback functions take the following syntax:

function([function arguments], callback_function)

Although it is not necessary for the callback function to be placed at the extreme right, it should be followed as a convention.

For example :

Say you want to fetch some resource from a server’s database,

connection.query(“SELECT * FROM authors ”, function(err, rows) {
if (err) throw err;
for (var i in rows) {
result = rows[i].name;
}
connection.end();

Here the first parameter of the inbuilt function “query” is the query string to be executed on the database and the second parameter is an anonymous callback function(A function without a name) that is invoked only after the query string is executed. Our callback function has 2 arguments, error and the result of the database execution.

Note: It is important to check for errors in a callback function so it would stop execution when necessary.

One of the major drawbacks of callback functions is that, in some scenarios, we may have to deal with a callback that is nested in another callback which is nested in another one and so on! In such cases, the code may become messy and unreadable.

Callback functions are now antiquated and you may mainly come across them in old JS codes.

Promises

promiseFunction(){
.then(callback_function_for_fulfilled_promise)
.catch(callback_function_for_rejected_promise)
}

As you can see from the code above, callback functions are used in promises too. These Promise functions do not return any specific value, rather they return a promise.

But first, what exactly is a promise?

Promise is a JavaScript object that holds a substitute of a value that is not known at that point of time. It can fill in the actual value at any point in the future. A promise has three states: pending, fulfilled and rejected. Depending upon the state that it is in, the respective callback function is called.

Example :

const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
})
.then(()=>{
console.log(“Successfully fetched data and displayed”)
})
.catch(`Some error occurred while trying to get data ${err}`)
makeRequest()

The code above is an example of chained promises. This is similar to nested callbacks. If any of the promises in the chain is not fulfilled, it’ll directly execute the code in the catch block.

Note: To run the promises in parallel, just create an array of promises and run them like this:

Promise.all(promiseArray)

As you can see, it’s much cleaner and easier to read as compared to nested callbacks. But using async and await, this would become much more concise.

Once you have understood the concept of promises, we can now move on to the next topic, i.e. async and wait.

Async and await

Async functions take the following syntax :

async function() {
await [Asynchronous Action]
}

Note: The await keyword only work inside of async functions. Using it any other function would result in an error. There can be multiple await keywords within a single async function.

Let’s see how async functions work by using the same example as that of promises.

const makeRequest = async () => {
console.log(await getJSON())
console.log(“Data successfully displayed!”)
}
makeRequest()

As promises can return errors, it is necessary to enclose the above code within try/catch blocks.

const makeRequest = async () => {
try{
console.log(await getJSON())
console.log(“Data successfully displayed!”)
}
catch(error){
console.error(`Error in fetching data ${error}`);
}}
makeRequest()

As you can see, it looks like a simple synchronous code and is comparatively easier to maintain as compared to promise functions.

Async functions always return a promise implicitly even if you don’t return a promise explicitly and the resolve value is whatever we return from the function.

As good and nice as it looks, this methodology does have some downsides to it:

  1. Increases time complexity: The await keyword blocks the execution of all other tasks that follow it, although it allows other tasks to run in the background. If there are a lot of await keywords, it could slow down the execution of the code. Hence it is advisable to write smaller async functions.
  2. No browser support for older browsers like Internet Explorer and Opera Mini.

Conclusion

Resources :

I am a recent graduate and work as a software engineer at Persistent Systems. I believe in sharing with the community whatever I learn and I love to write!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store