Making JavaScript Asynchronous
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 are nothing but functions that are passed as parameters to another function which starts executing the code in the background. Callbacks make sure that a code gets executed only after the background code is executed.
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
To overcome the drawback of callback functions, promises was the new feature to be introduced in JavaScript. Promises help write asynchronous code clearly and to the point. Promises take the following form:
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
The keyword ‘async’ is put in front of a function name to tell the compiler that the following code is an asynchronous function. The second keyword ‘await’ is used to invoke the asynchronous code. Async functions,too, return promises instead of the concrete value.
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:
- 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.
- No browser support for older browsers like Internet Explorer and Opera Mini.
Conclusion
Async/await is not better or something that was introduced to replace promises, it is merely just an improvement. Some people still prefer using promises to achieve asynchronous code. Whether to use promises or async/await is simply a matter of personal choice.
Resources :