Asynchronous Programming
Handling promises and async/await, when dealing with APIs and the DOM.
As front-end developers, you’ll often need to interact with APIs or perform tasks that take time, like fetching data or waiting for user actions. Understanding how to handle these tasks effectively with promises and async/await
will help you write cleaner and more responsive code.
We’ll cover:
- Promises: How to create and manage promises to handle asynchronous operations.
- Async/Await: A more readable way to write asynchronous code, making it easier to work with promises.
- Real-World Examples: Using promises and
async/await
to handle API calls and interact with the DOM.
Promises (es2015.promise
)
Promises are a key concept in asynchronous programming, allowing us to handle operations that may complete at a later time. A promise represents a value that may be available now, in the future, or never.
The es2015.promise
module defines the Promise
type, which is used for handling asynchronous operations in TypeScript. This module provides type definitions for promises, allowing you to work with asynchronous code in a type-safe manner.
Why It’s Important:
- Simplifies asynchronous code with a clean, consistent API.
- Helps in handling complex operations like API calls, file reads, etc.
Creating a Promise
Let's start by creating a simple promise:
ts
constmyPromise = newPromise <string>((resolve ,reject ) => {constsuccess = true;if (success ) {resolve ("Operation was successful!");} else {reject ("Operation failed.");}});
In this example:
- We define a new promise that will either resolve with a success message or reject with an error message.
- The promise is typed to return a
string
.
Handling a Promise
Once we have a promise, we can handle its outcome using .then()
and .catch()
:
ts
myPromise.then((result) => {console.log(result); // "Operation was successful!"}).catch((error) => {console.error(error); // "Operation failed."});
*Async/Await
While promises are powerful, chaining multiple .then()
calls can sometimes lead to less readable code. TypeScript offers async/await
as a more intuitive way to work with asynchronous operations.
Writing an Async Function
An async
function automatically returns a promise. Inside the function, we can use await
to pause execution until a promise is resolved or rejected.
ts
async functionfetchData ():Promise <void> {try {constresponse = awaitfetch ("https://api.example.com/data");constdata = awaitresponse .json ();console .log (data );} catch (error ) {console .error ("Error fetching data:",error );}}
In this example:
- The
fetchData
function is marked asasync
, meaning it will return a promise. - We use
await
to pause execution until thefetch
promise resolves, then parse the response as JSON. - If an error occurs, it is caught in the
catch
block.
Working with the Error type
In the previous example if we wanted to display the error message to the user we would normally access it through error.message
but if we try to do this in Typescript we will get a warning that error
is unknown
ts
async functionfetchData ():Promise <void> {try {constresponse = awaitfetch ("https://api.example.com/data");constdata = awaitresponse .json ();console .log (data );} catch (error ) {'error' is of type 'unknown'.18046'error' is of type 'unknown'.alert (. error message );}}
This is because the error object returned could be of any type and may not have an error property. Consider these possibile shapes for the error being returned
ts
// Most Likely an object with a message property, this is commonerror = {message: '500: Internal Server Error';}// Could also be just a stringerror = 'Something went wrong'// Or an arrayerror = [500,'Internal Server Error']
To prevent accessing a property that may not exist TypeScript will warn us and so we should use a type assertion and an if block to check the message exists and then access it for display.
Example:
ts
async functionfetchData ():Promise <void> {try {constresponse = awaitfetch ("https://api.example.com/data");constdata = awaitresponse .json ();console .log (data );} catch (error ) {if ((error asError ).message ) {alert ((error asError ).message );}}}