In this article we'll be covering callbacks, promises and async/await as these are the ways to deal with asynchronous data.
To understand asynchronous, you'll first have to understand synchronous.
What is synchronous?
Synchronous is when something is done in sequence. In programming terms synchronous is when a bunch of code are executed one after the other.
For example
function A() {
console.log("Task 1");
console.log("Task 2");
console.log("Task 3");
}
A();
In the example above the code will run synchronously.
Task 1
Task 2
Task 3
It prints Task 1
, Task 2
, and Task 3
one after the other. It will wait for every line to complete its execution.
What is asynchronous?
Asynchronous is nothing but just the opposite of synchronous. You keep doing things while others are getting completed.
What do you think the code will print?
console.log("Task 1");
setTimeOut(() => {
console.log("Task 2");
}, 1000);
console.log("Task 3");
well the above code will print
Task 1
Task 3
Task 2
Why does it print that way? It is because setTimeout function is not called immediately. It is called after 1 second. Till the time it waits the third console.log() gets printed.
Why Asynchronous Javascript?
When a JavaScript code is executed, the synchronous code blocks further execution until it completes what it's doing. Code which takes long to complete can make the UI/server unresponsive until the function has returned, which result in a terrible user-experience.
For instance: You want the latest comments of your blog from a server and render in your blog, and it is done synchronously, then a visitor who is in your blog site won't be able to do other things until those comments are loaded. Which indeed could cause a long delay before they could read the comments in your blog.
To understand the above example, follow the code:
const comments = loadCommentsFromDatabaseSync();
displayBlogInWebPage();
In the above code, in order to display the blog on website, the website will first have to wait for loadCommentsFromDatabaseSync()
to get complete. Once loadCommentsFromDatabaseSync()
is completed then only it will display the blog in the web page.
By using async
const comments = loadCommentsFromDatabaseAsync();
displayBlogInWebPage();
In the above code displayBlogInWebPage()
will not wait for the completion of loadCommentsFromDatabaseAsync()
.
We should use asynchronous programming when performing expensive and time-consuming operations.
Different ways of Asynchronous programming
In javascript, there are three ways to accomplish asynchronous programming, namely callback
, promises
and async/await
.
Let us get in detail with example.:
Callback
What is a callback?
A callback is a function which is executed after a subsequent function has finished executing.
As JavaScript functions are also a type of object
and they can be passed as an argument while calling a function so much like any other objects like string
, number
etc.
Example:
function addTwoNumbers(a, b, callback) {
console.log("Addition of two numbers: ", a + b);
callback();
}
function print() {
console.log("This must be printed after addition to the console");
}
addTwoNumbers(2, 3, print);
The output of the above code:
Addition of two numbers: 5
This must be printed after addition to the console
In the above example, we have two functions:
addTwoNumbers(a,b,callback): This functions is called with there arguments a
,b
and callback
, where a
and b
are numbers
while the third argument callback
is a function. The addTwoNumbers() prints the addition of the two numbers, and as soon as that completes its execution, the callback function is fired!
This type of function is also known as the
callback function
.
print(): As soon as addTwoNumbers() completes its execution and call the callback function this print() will get called and prints its output to the console.
Callbacks are mainly used for handling asynchronous operations like – making an API request to the server, fetching/writing some data from/into a file, registering event listeners etc. Callbacks are used for the mentioned operations. Depeneding on the result of the operation the callback function will be executed.
Promises
To understand the basic of promises please check-out Basic of Promises.
This will be the continuation of the above link.
basic syntax of promise in javascript.
let promise = new Promise(function (resolve, reject) {
//resolve or reject is done here
});
As discussed in the basics of Promises section promises have three states, and the states are self-explanatory:
Pending: Pending is a state when the promise is neither resolved or rejected. It will continue to remain indefinitely pending
unless it gets resolved or rejected.
Resolved: A promise is resolved when the resolve method is called from the promise. The resolved promise will be consumed in the .then()
section.
Rejected: If the reject function was called from the promise, then the promise is rejected. If the promise is rejected, then it should be consumed in the .catch()
section.
Consuming a promise
Please check the above link in order to see consuming promises.
Chaining of promises
In order to understand the concepts of Async/await, one has to understand the core usage of promise, which includes chaining of promises
.
When a .then() method returns a promise, then the subsequent executions of the
.then()
are suspended until the current promise block is resolved.
let firstPromise = new Promise(function (resolve, reject) {
setTimeout(resolve, 1000, "Pratap");
});
let secondPromise = new Promise(function (resolve, reject) {
setTimeout(resolve, 2000, "Prasar");
});
let thirdromise = new Promise(function (resolve, reject) {
setTimeout(reject, 3000, "Error");
});
firstPromise
.then((x) => {
console.log("First Promise after 1 sec: ", x);
return secondPromise;
})
.then((x) => {
console.log("Second Promise after 2 sec: ", x);
return thirdromise;
})
.catch((e) => {
console.log("Third Promise after 3 sec: ", e);
});
The output of the above code:
First promise after 1 sec: Pratap
Second promise after 2 sec: Prasar
Third promise after 3 sec: Error
Explanation of above code.
Let us understand step by step:
- We have initialised 4 promises
firstPromise
,secondPromise
,thirdPromise
andfourthPromise
. For the first instance, all four promises are racing toward resolve/reject. - After 1 second the
firstPromise
gets resolved as we are calling theresolve
method in the promise initialization and this gets printed in the consoleFirst promise after 1 sec: Pratap
. Then we return another promisesecondPromise
. - After 2 seconds the
secondPromise
also getsresolved
andSecond promise after 2 sec: Prasar
gets printed to the console. We then returnthirdPromise
from thesecondPromise
. - But, after 3 seconds the
thirdPromise
gets rejected as we calledreject
in thethirdPromise
initialization.Third promise after 3 sec: Error
gets printed to the console.
Promise.all
Promise.all
accepts an array of promises, and will attempt to fulfil all the promises. If any of the promises are rejected, then the promise will exit.
In order to fulfil multiple promises, promise.all
was introduced. It is widely used in javascript frameworks where we want to get data from multiple APIs
. So rather then calling the promises individually you'd better want to use Promise.all()
method and handle the result based on the status of the promises.
An example of promise.all
:
const arrayOfPromises = [new Promise(promise1), new Promise(promise2)];
function runAllThePromises() {
Promise.all(arrayOfPromises).then(showSuccessMessage).catch(showErrorMessage);
}
arrayOfPromises();
Promise.race
promise.race()
method returns a promise which fulfills or rejects as soon as one of the promises in an array gets fulfills or rejects.
We can understand promise.race()
as the realtime race.
Suppose five people are participating in a race and any of them crosses the winning line then the race ends. Also, if any of them gets injured, then also the race gets end.
const firstPromise = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "First Promise");
});
const secondPromise = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "Second Promise");
});
Promise.race([firstPromise, secondPromise]).then((value) => {
console.log(value);
});
Output:
Second Promise
Explanation:
We passed firstPromise
and secondPromise
as an array in the argument of Promise.race()
. As secondPromise
gets resolved faster than firstPromise
so Second Promise
gets printed in the console.
Async/Await
Async/Await is one of the most recent additions to the JavaScript language which is part of ES8.
Async/Await is syntactic sugar on top of promises which makes asynchronous code easier to write and to read. The async code is written just like the synchronous code. It is just a special syntax to work with promises in a more synchronous-type manner.
Basic function:
const getFruit = () => {
return "Mango";
};
The above snippet just a basic javascript code which returns Mango
.
async
Now, let us convert the above code to promise using async
keyword.
const getFruit = async (name) => {
const fruits = ["Apple", "Banana", "Mango"];
return fruits[name];
};
Now, in the function above, just adding async
keyword in front of a function, not the function will return a Promise
.
getFruit("Apple").then((res) => {
console.log(res);
}); // prints: Apple
await
The await
keyword is used to wait for a Promise to get resolved. await
can be used inside an Async function block only. It makes JavaScript wait until the promise returns a result. It only makes the async function block wait and not the whole program execution.
async function demonstrateAsync() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise is ressolved!"), 1000);
});
// wait until the promise returns us a value
let result = await promise;
console.log(result);
}
demonstrateAsync();
The output of the above code:
Promise is ressolved!
await
should be used within anasync
block only.
await
can't be used in top-level code
Catching error in async/await
Handling errors in async/await is pretty straight forward. In order to handle an error, we just have to wrap inside a try..catch
block.
async function thisThrowsError() {
try {
let response = await fetch("http://invalidUrl");
} catch (err) {
console.log("Error: ", err); // Invalid url
}
}
thisThrowsError();
Any error in the scope of try
block will be handled in the catch
block.
Conclusion
In this blog, we have completed the different ways of asynchronous programming in Javascript and their usage.
Due to the addition of asynchronous programming in javascript, it makes it possible to express waiting for long-running actions without blocking the execution of code. In javascript, it is typically implemented using callback
, promises
and async/await
.
Programming asynchronously in Javascript is made easier by using promises and async/await, which allow us to write an asynchronous program as if it were synchronous.
FURTHER READING
- Things to keep in mind before starting Javascript framework
- Strapi.js - Open Source Node.js Headless CMS
- var, let, and const – Why to avoid var 😷 and how to put the other two to good use? - Javascript
💌 If you’d like to receive more tutorials in your inbox, you can sign up for the newsletter here.
Top comments (1)
You can also handle the error without
try
block like this: