Have you ever visited the Spotify Web App?? How does it even loads an artist data and play the music at the same time? Even while the music is playing, you can visit a different tab and still work properly!!
Of course the app was programmed in the way so that it handles multiple tasks at a time. But in we've heard in programming, everything is executed sequentially, line-by-line, so what the hell is going on ??
This is where the concept of asynchronous programming comes in.
What's wrong with synchronous programming?
Synchronous programming basically means executing your code line by line. What comes first is executed first, no matter how long it takes.
Here's a simple example in JavaScript
console.log('This is the first line');
const data = readFile('someFile.txt')
console.log(data)
const response = requestAnApi('some url')
if(response.status==404){
console.log('You got a 404 response');
} else{
console.log('You got a valid response')
}
It's pretty straightforward, and it obviously makes sense that we want some certain tasks to be performed earlier than others.
But this isn't something we would want at all times.
What would you do if you're requesting some data from an API that takes more than 10 seconds, only to get a 404 response?? The rest of the code would literally stop working.
I've created a pen where we will try to see what's wrong with synchronous programming through a slow HTTP request
While you requested the data, the page became unresponsive. That shouldn't be the case because requesting some data and editing a text is totally different!!
So what is the workaround for this??
Asynchronous programming
Yes this is what this blog is about. What is asynchronous programming and how is it useful?
It is basically a way to enable our code to respond to all the events and still run a long, time-taking task on the background. The moment this long running task is finished, we are presented with that result.
And yes it comes very handy in use cases like in the pen above, where we want to get some response but also edit the text at the same time.
Easy-peasy, but HOW do we do it practically ?
Many languages have a keyword called async
that enables us to write asynchronous code. It's also available in JavaScript but before we get to that, we need to know what are Promises.
Unlike the case of politicians, JavaScript promises are actually executed and they always return something. Whether it is an error or some helpful response, they never stop until the task is fully executed.
Syntax and examples
In JavaScript, promises are defined just like an instance of a class object along with a function inside that always give a response.
The response is either 'resolved' or 'rejected' depending upon the execution of the function.
const adultPromise = new Promise((resolve,reject) => {
// let's say this takes long time to execute
let age = 18;
let isAdult = age>18 ? true : false;
if(isAdult){
resolve('You can vote')
}
reject('You cannot vote')
})
This is a simple promise that checks whether the age is more than 18 or not. To execute and get result of this promise, we get the methods then
,catch
and finally
This is just like try
,catch
and finally
for error handling.
The way this promise is executed is -
adultPromise
.then(value => {
console.log(value);
})
.catch(err => {
console.error(err)
})
.finally(() => {
console.log('Decision has been taken')
})
If the promise is resolved, the then
method would be executed and if the promise is rejected, catch
is executed like an error.
No matter whether the promise is resolved or rejected, the finally
method is executed at any case.
We don't need any return statement here because on calling any of the two functions will finish the execution automatically, returning the data what's inside.
And yes, we can return anything from the promise, and I mean literally anything that are available - functions, objects, numbers,3 anything !!
But what if I want to pass my own arguments in the promise??
That would simply mean we wrap a function around the promise, get the arguments, maybe do some logic and return the promise. To demonstrate above example only -
const checkAdult = (age) => {
return new Promise((resolve,reject) => {
if(age>18){
resolve('You can vote!');
}
reject('You cannot vote!');
})
}
checkAdult(21)
.then(value => console.log(value))
.catch(err => console.error(err))
/* This prints out You can vote! */
checkAdult(15)
.then(value => console.log(value))
.catch(err => console.error(err))
/* This prints out You cannot vote! as an error */
Yes we can use the promise again and again just like any other variables if defined properly.
Promises and method chaining
We can perform method chaining in Promises just like any class instances. Note that this is only possible for then
method.
checkAdult(21)
.then(value => {
console.log(value);
return 21-value;
})
.then(diff => {
console.log(`It's been ${diff} years since you turned 18`)
})
If a then
block returns some value, we can get that value by chaining another then
block. Here the diff
variable contains the difference of the ages as returned by it's predecessor.
Is it wrong to have multiple then
blocks for the same promise?
It's not uncommon to see multiple then
statements chained together. But still, having tens of then blocks or a then
block inside another then
block can be very messy.
/* This code can run perfectly giving the output that we want, it's very messy to read
This is what is called as 'Promise hell'.
*/
datFetch()
.then(response => {
dataBase('User')
.then(user => {
response.verify()
.then(check => {
...
})
})
})
What is async
in JavaScript?
The async
and await
keywords in JavaScript are just a 'syntactic sugar' around promises. In other words, it makes the promises more readable.
Apart from the top-level await feature introduced in ES2020, they are only possible to use in functions or class methods.
Let's use our above example in an async function.
async function main(){
try{
const decision = await checkAdult(21);
console.log(decision)
} catch(err){
console.error(err)
} finally{
console.log('Decision is taken');
}
}
main()
This is even more readable than those then
,catch
blocks. I've wrapped the execution inside the try-catch blocks. It's more of a convention to follow this way.
The way this code executes is as follows :
1) The main function is declared as an asynchronous function. All of the code is executed in try-catch statements.
2) In the try block, the promise is called through the await
statement and the result is given to decision
variable.
3) The decision is logged into the console. If the promise throws any error, the control automatically shifts to catch
block.
4) At the end, finally
block is executed, logging that the decision is taken and the function is finished.
How exactly does await
work?
While the promise is called like a function using await
keyword, it's value is returned to a variable (in this case decision
).
This does not stop the code from executing, instead, the code next line to it is executed and so on.
The promise is also executed in the background and the moment its value is available, it's returned to the assigned variable and all the code related to it is executed.
If all the further code is dependent on this variable, then and only then, the flow is 'put on hold' until the promise is executed.
This was all about asynchronous programming in a basic sense. More complicated stuff (like fetching an API and handling user input) can be executed using the same logic. Hopefully, this gave a better idea about how this works in JavaScript.
Until next time folks !! ✨
Top comments (0)