Tired of untangling tangles of asynchronous code in your JavaScript or TypeScript projects?
Do you wish there was a method to make your asynchronous processes perform like a well-oiled machine? If that's the case, we've got a solution for you: promises - the key ingredient in any excellent asynchronous recipe. They let you manage async actions in a more orderly and understandable manner, allowing you to focus on the more enjoyable aspects of coding. We'll take you on a tour through the wild and beautiful world of promises in this article.
๐ค What exactly is a promise?
In JavaScript, a promise is a means to handle asynchronous actions in a more organized, understandable, and cleaner manner. They enable you to build code that operates in a predictable, sequential manner even though the actions themselves are taking place in parallel.
Promises are created by passing a function as a parameter to the Promise
constructor. This function is known as the executor function, and it is in charge of carrying out the asynchronous operation. There are two arguments to the executor function: resolve
and reject
. These functions are used to either fulfill (resolve) or reject the promise.
Take the following example, where a promise is created with an executor function which waits 3 seconds and then resolves the promise with the value of "Hey!๐My name is Bruno.":
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Hey!๐My name is Bruno.");
}, 3000);
});
You may use the then()
method after you've created a promise to tell it what to do when it's resolved or rejected. As arguments, it takes two functions: one for the resolve case and one for the refuse case.
Consider the following scenario, in which the resolve case logs the message "Hey!๐My name is Bruno.", whereas the reject case, on the other hand, logs an error message:
myPromise.then(
(message) => {
console.log(`The promise has been resolved: ${message}`)
},
(error) => {
console.log(`The promise has been rejected: ${error}`)
}
);
โ Chaining promises
Chaining promises allows you to create a sequence of asynchronous operations that execute sequentially/one after the other.
myPromise
.then((message) => {
console.log(message); // Hey!๐My name is Bruno.
return message + " I am a software developer."
})
.then((message) => {
console.log(message); // Hey!๐My name is Bruno. I am a software developer.
})
The above promise is chained, with the first then()
logging the first version of message
, whereas the second then()
logs message concatenated with the string from the return
statement.
Let's now move on to some use cases for promises.
๐ง Where can you use a promise?
There are different cases and scenarios where promises can be used to perform asynchronous operations.
Handling HTTP requests
Handling HTTP requests can be done in a clean and easy-to-understand way. We will illustrate an example using the Axios library, which essentially does the same as the already in-built fetch()
:
import axios from "axios";
const API_URL = "dummyapithatdoesnotexist.com/data";
axios.get(API_URL)
.then((response) => response.json())
.then((jsonData) => console.log(jsonData)) // data from the API
.catch((error) => console.log(`There has been an error: ${error}`));
Notice the 'catch()' method at the end of the promise.
'catch()' is used to handle any errors that occur while attempting to resolve the promise. It is highly valuable and an important aspect of asynchronous operations since it provides context for why they did not succeed (for example, when there is no internet connection or the server is down).
Of course there are other scenarios where you can use promises, but this one was a good illustration of one widely-known.
๐คบ Promises in JavaScript vs Typescript
You might be questioning yourself now: But... what is the difference between promises in JavaScript and Typescript? ๐ค
Well, that is a good question that we will cover now, of course.
To create promises, both JavaScript and TypeScript utilize the Promise constructor, and both languages utilize the then()
method to indicate what should happen when the promise is resolved or refused.
One significant distinction between JavaScript promises and TypeScript promises is that in TypeScript, you make use of greater type checking, which can assist prevent mistakes and bugs in your code. That can avoid a lot of headaches at the end of the day. The stronger typing allows you to declare the types of values that a promise will resolve or reject, whereas JavaScript relies on runtime type checking. This is an important aspect to keep in mind while using JavaScript or Typescript in your project.
An example of a typed promise in Typescript would look something like this:
import axios from "axios";
const API_URL: string = "dummyapithatdoesnotexist.com/data";
const myPromise: Promise<string> = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Hey!๐My name is Bruno.");
}, 3000);
});
When specifying the then()
and catch()
scenarios, you would also type them as the following:
import axios from "axios";
const API_URL: string = "dummyapithatdoesnotexist.com/data";
axios.get(API_URL)
.then((response: Response) => response.json())
.then((jsonData: any) => console.log(jsonData)) // data from the API
.catch((error: any) => console.log(`There has been an error: ${error}`));
In Typescript, you can also make use of the async
, which allows you to indicate that the operation is asynchronous, and await
, which pauses the execution of the function until the promise is resolved.
import axios from 'axios';
const API_URL: string = 'dummyapithatdoesnotexist.com/data';
async function getData() {
try {
const response: AxiosResponse = await axios.get(API_URL);
const jsonData: any = response.data;
console.log(jsonData);
} catch (error) {
console.log(`There has been an error: ${error}`);
}
}
getData(); // logs the data from the API
The AxiosResponse type is imported from the axios library and used to type the response variable in this example. To get the JSON data from the API response, utilize the response.data property. The jsonData variable is of type any, suggesting that it may hold any type of value. The error
variable is also typed as any, implying that it might be of any type as well.
The async
keyword identifies the getData()
function as asynchronous, and the await
keyword pauses the function's execution until the promise is resolved. If an error happens during the request, the catch()
block detects it and logs it to the console.
Thank you for reading!๐
After having gone through all of these bits and bytes of promises in JavaScript and Typescript, you should now be able to apply them either untyped or typed in your code. Congratulations ๐ and I hope it was helpful for you!๐
I'd like to thank you for reading my article and invite you to follow me on Dev.to, as well as my other platforms:
GitHub: https://github.com/bcostaaa01
Twitter: https://twitter.com/bruno2001costa
I look forward to seeing you on my next one!
Until then,
Happy Coding!๐ฉโ๐ป
Top comments (4)
Nice read!
ps. you can use async/await in javascript as well since ecmascript 2017
Thank you for the positive feedback and reminder, Rense!๐
Great article! , you have covered Promise and Asynn-await, in a very clear and simple way...so far i didn't read such a clear and simple explanations on these topics...
Thanks Bruno!
Hey yasirmeraj, I am happy to hear that! ๐ Thank you for the read, and the positive feedback!