- Published on
Asynchronous in Javascript
- Authors

- Name
- James Yoo
Prerequisite
Javascript is a single-threaded, and all codes are executed in a sequence, not in a parallel fashion. In other words, unlike other languages such as Java, there is only one thread in Javascript, and it can be blocked by an expensive operation. (For example, when you call API to retrieve data or CRUD operations, it takes time). To overcome this situation, the concept of asynchronous is useful and must be used.
Description
Asynchronous methods are handy as we do not wait for an operation to be completed before going forward. Once it is finished, the result will be returned and codes can be executed based on the response. In Javascript, we can use callback, Promise and/or async / await to do asynchronous operations.
- Note: Asynchronous implies executing one line, but not waiting for it to be returned. It is not equivalent to Concurrency or Parallelism
Callback
Description
Callback is the beginning point of an asynchronous operation in Javascript. In brief, developers and engineers implement callback in a function so that it can be called once a time-consuming operation is finished.
Example 1
In the first example, alert will be executed, and further operation will be done in the second function.
function first(callback) {
alert("Hi there. How are you doing today?");
callback();
}
function second() {
alert("I am doing great. How about you?");
}
first(second);
Note that second in first(second) is not followed by parenthesis.
Example 2
In the second example, assume that we called an API. Based on the result, we will pass it to the next function.
function first(callback) {
callApi(..., function(names) {
let firstName = names.split(" ")[0];
let lastName = names.split(" ")[1];
callback(firstName, lastName);
})
}
function second(firstName, lastName) {
alert("Hello, firstName + " " + lastName);
}
Nested callbacks are possible, but it is hard to read.
Callback hell
Therefore, we say it is a callback hell. To solve this, Promise or async / await can be used.
city.getWardList(cityName, function(ward, err) {
if (err) console.log(err);
else {
wards.forEach(function(ward, index) {
ward.getSchoolIds(ids, function(res, err) {
ids.forEach(function(id, index) {
schoolList.getSchoolNameById(id, function(name) {
...
})
}))
})
})
}
})
Promise
Description
Another method for asynchronous operation is a Promise. It returns another a state of Promise (pending, fulfilled or rejected) in a synchronous fashion, and it supplies the final (return) value when available. Let's see more through examples.
- NOTE:
Promiseis not working on Internet Explorer
Example 1
The below example is based on MDN article
const promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
});
promise1.then(function(value) {
console.log(value); // "Success!"
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'It is resolved now');
});
promise2.then(result => {
alert(result);
});
promise1 shows how Promise resolves an object (in this case, String), and shows a result via console.log. promise2 is almost the same as promise1, arrow functions are used for a reference. After 3 seconds, it is resolved, putting a parameter, 'It is resolved now' in the function.
Example 2
Example 2 focuses on Promise chaining.
const powerOfTwo = number => {
return Promise.resolve(2 * number);
}
Promise.resolve(0).then(powerOfTwo) // 1
.then(powerOfTwo) // 2
... // 4
.catch(error => alert(error))
.finally(result => {
console.log(result);
})
powerOfTwo is a function for nth power of 2. The important parts are then, catch and finally. then is used to receive a return value, and catch is called when there is an error. Two ways to use catch inside a promise are suggested.
- Inside of promise, throw
Error. For example,
new Promise((resolve, reject) => {
throw new Error("Encountered an error");
}));
- OR,
rejectit
new Promise((resolve, reject) => {
reject(new Error("Encountered an error"));
})
Last but not least, finally can be used so that some actions must be taken whether all code is executed with or without a problem.
Promise.all
One of the strengths in using Promise is that we can do multiple asynchronous operations in a parallel manner (please check the note as well). This also implies that we can easily identify and solve a problem when any of the Promise is failed.
const promise1 = updateUserProfile(userID, options);
const promise2 = updateDocument(userID, options);
Promise.all([promise1, promise2]).then(values => {
console.log(values);
})
NOTE
- Until I wrote this, I thought Promise.all always run things in parallel. It was so surprising to know that it is not guaranteed, even though a single-core CPU is not popular anymore.
Async & Await
Description
The final method I will introduce is Async & Await. While each developer and engineer chooses a different method depending on their preference and circumstance they encounter, I prefer using Async & Await as it is more straightforward.
Example 1
async function hello() {
let result = await fetch("........");
let user = await result.json();
}
async function world() {
return "hello world";
}
As you see from the above, async is mandatory if you need to use await for an asynchronous operation. However, async does not always need await.
Example 2
const example = async () => {
let promiseVariable = new Promise((resolve, reject) => {
resolve("hi there");
});
let result = await promiseVariable;
}
Example 3
const asyncFunction = async () => {
const docs = await getDocuments();
const newValue = "...";
Promise.all(
docs.map(async doc => {
const serverTime = await getServerTime();
const result = await updateDoc(doc, newValue, serverTime);
})
)
}
Through example 2 and example 3, we can find that Promise.all with async and await can be used together, and it is quite useful to do multiple asynchronous operation clearly and efficiently.