`async` in JavaScript and TypeScript
async function throwError(): Promise<never> { throw new Error('Error'); }
Without `async`, you would have to manually instantiate Promises:
function getValueOld(): Promise<number> {
return Promise.resolve(42);
}
Steps
Step 1: Convert a function to `async`
Add `async` before `function` or before the parameters in an arrow function. This makes the function asynchronous and gives you access to `await` within the function.async function fetchData() {
// asynchronous code here
}const fetchDataArrow = async () => {
// asynchronous code here
};
Step 2: Use `await` for Promise-like values
`await` pauses the execution of the function until the Promise is handled. It works with any object that has a `.then()` method—a "thenable"—not just native Promises.async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
}In this example, `await` first waits for the `fetch()` Promise, then for the `.json()` Promise. The code reads like synchronous instructions, but does not block the event loop.
Step 3: Catch errors with `try`/`catch`
Use standard `try`/`catch` blocks within `async` functions. This is a familiar, intuitive way to handle errors, unlike `.catch()` chains where errors are easily missed.async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
Step 4: Catch errors at the call site
Because `async` functions return Promises, you can also use `.catch()` at the call site. This is useful if you want to keep error handling outside the function.fetchData().catch((error) => {
console.error('Error in fetchData:', error);
});
Step 5: Use `await` at the top level in modules
In modern JavaScript and TypeScript, you can use `await` outside an `async` function, as long as you are working in a module. This simplifies initial asynchronous setup code.// Only in modules (ESM)
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
Step 6: Transpile for older environments
TypeScript 2.1 and higher transpiles `async`/`await` to ES3/ES5 code with generators and a Promise polyfill. Without a Promise polyfill, the generated code will not work in ES3/ES5 environments.tsc --target es5 --lib es2015.promise,dom app.ts
Step 7: Use `async`/`await` in Node.js
Node.js has supported `async`/`await` natively since version 8. Use it for file operations, database connections, and HTTP requests to avoid blocking the event loop.import { readFile } from 'fs/promises';async function readConfig() {
try {
const data = await readFile('./config.json', 'utf-8');
return JSON.parse(data);
} catch (error) {
console.error('Cannot read config:', error);
return {};
}
}
Step 8: Combine with Promise.all for parallel execution
`await` serializes operations. For parallel tasks, use `Promise.all` with `await`.async function fetchMultiple() {
const [users, posts] = await Promise.all([
fetch('https://api.example.com/users').then(r => r.json()),
fetch('https://api.example.com/posts').then(r => r.json())
]);
return { users, posts };
}
Verification
Check whether your environment correctly supports `async`/`await`:node -e "async function test() { return 'ok'; } test().then(console.log)"
This should print `ok`. For TypeScript:
tsc --version
This should show version 2.1 or higher for ES3/ES5 transpilation.
Common issues
Issue 1: `await` outside `async` function
Error message: `SyntaxError: await is only valid in async functions and the top level bodies of modules`Solution: Place `await` inside an `async` function, or ensure your file is a module by setting `"type": "module"` in `package.json` or by adding an `export` statement.
Issue 2: Thenables with incorrect implementation
Symptom: Infinite waits or unexpected behavior with `await`.Solution: Check that the object has a correct `.then()` method that calls the `resolve` and `reject` callbacks. A thenable that never resolves or rejects hangs the `await` expression forever.
Problem 3: TypeScript does not transpile to older targets
Error message: `Type 'Promise<...>' is not assignable to type 'void'` or runtime errors in older browsers.Solution: Use TypeScript 2.1 or higher with `--target es5` and add a Promise polyfill. For Node.js 4-7 you need a generator polyfill, or upgrade to Node.js 8 or higher for native support.
Version history
TypeScript introduced `async`/`await` in version 1.7, depending on ES6 generators in the runtime. TypeScript 2.1 extended support to ES3/ES5 via transpilation with a Promise polyfill. The implementation follows the ES2016 specification (ES7), which guarantees interoperability with vanilla JavaScript. JavaScript itself has supported `async`/`await` natively since April 2017 in modern browsers.The combination of `async` and `await` has fundamentally changed the way asynchronous code is written. Frameworks such as Express, NestJS, and React are designed with this construct in mind. The syntax reduces errors, improves readability, and simplifies testing — but the underlying Promises and the event loop still determine how your code actually behaves.