Asynchronous programming in TypeScript has been greatly simplified with the introduction of async/await functions

Arnold Feng
Grid Solutions
Published in
2 min readNov 30, 2023

--

Asynchronous programming in TypeScript has been greatly simplified with the introduction of async/await functions. While async/await makes code look synchronous, really knowing how it works gives us power to use it to its fullest.

In this article, we’ll dive into asynchronous functions, exploring their syntax, return types, and advanced usage using examples encountered on my project.

Understanding Basic Asynchronous Function Syntax

At its core, an async function is marked by the async keyword, signaling its asynchronous nature. Even when seemingly returning synchronously, the function inherently returns a Promise:

async function fetchData() {
return 'data';
}

// Inferred return type: Promise<string>

This minimal example sets the stage for the async/await journey.

Unwrapping Values with Await

One of the defining features of async/await is the await keyword, which retrieves the resolved value of a Promise. Consider the following example fetching data from database:

async function GetDataByDate(date: Date) {
const order = await DataService.getOrderByDate({ date });
const contracts = await DataService.getContractByDate({ date });
return { order, contracts };
}

Here, we call the getOrderByDate method and getContractByDateof the dataService with a specific date parameter and returns the fetched data.

Resolving Promises

While await unwraps the resolved value of a Promise, it's important to understand how to create and return resolved Promises explicitly. The Promise.resolve method facilitates this:

async function getOrderByDate(date: Date) {
const resolvedValue = await Promise.resolve({
date: new Date(2023, 10, 23),
orderedQuantity: 100000
});
return resolvedValue;
}

This function utilizes Promise.resolve to create and resolve a Promise.

Handling Rejections and Promise Never Types

In the realm of async/await, handling errors is important. If an await encounters a rejected Promise, it throws an error. The return type, in this case, is inferred as Promise<never>:

async function getContractByDate(date: Date) {
const rejectedValue = await Promise.reject('Unexpected error happened during getting contract.');
return rejectedValue;
}

Leveraging Promise Utility Methods

asyncfunctions harmonize seamlessly with Promise utility methods like Promise.all. This enables parallel execution without altering the function's behavior:

type DataByDate = {
order: OrderByDate;
contract: ContractType;
};

async function GetDataByDate(date : Date) : Promise<DataByDate> {
try {
// Use Promise.all to concurrently fetch order and contract
const [order, contract] = await Promise.all([
DataService.getOrderByDate(date),
DataService.getContractByDate(date),
]);

return { order, contract };
} catch (error) {
throw error; // throw the error
}
}

This example showcases the ability to utilize Promise utility functions to enhance the efficiency of asynchronous code.

Conclusion

async/await in TypeScript is not just a syntax feature; it’s a paradigm shift that empowers developers to write more expressive, readable, and efficient asynchronous code. So, let’s continue leveraging the advantages of async/await to build robust, scalable, and future-ready applications.

Thank you.

--

--