Mastering Async Functions in JavaScript

Async functions are a new feature in JavaScript that make it easy to write asynchronous code in a synchronous-looking style. Learn how to use async functions with promises, generators, and other asynchronous patterns to create complex asynchronous programs.

Async functions are a new feature introduced in ECMAScript 2017 (also known as ES8) that allows you to write asynchronous code in a synchronous-looking style. They are built on top of promises and generators, and they make it easy to write asynchronous code that is easy to read and debug.



What are async functions?

Async functions are functions that are marked with the async keyword. When you declare an async function, it returns a special kind of promise that is resolved with the value returned by the function or rejected with any errors thrown inside the function.

Here is an example of an async function that returns a promise:

 async function getData() { const response = await fetch('https://api.example.com/data'); return await response.json(); }

You can use this function just like a regular promise-returning function:

 getData().then(data => { console.log(data); });

But the real power of async functions comes from the await keyword, which allows you to wait for a promise to be resolved before moving on to the next line of code.



Using await

The await keyword can only be used inside an async function. It works by waiting for the promise on which it is called to be resolved and then returning the resolved value.

Here is an example of using await to wait for a promise to be resolved:

 async function getData() { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } getData().then(data => { console.log(data); });

In this example, the fetch function returns a promise that is resolved with a response object. The response.json function also returns a promise, which is resolved with the parsed JSON data. The await keyword is used to wait for both of these promises to be resolved before moving on to the next line of code.



Error handling with async functions

Async functions make it easy to handle errors using try-catch blocks. Any errors thrown inside an async function will be caught by the catch block and turned into a rejected promise.

Here is an example of using a try-catch block inside an async function:

 async function getData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } catch (error) { console.error(error); } } getData().catch(error => { console.error(error); });

In this example, if either the fetch or response.json functions throw an error, it will be caught by the catch block and logged to the console. The rejected promise will then be caught by the catch block attached to the getData function call.



Combining async functions with other asynchronous patterns

Async functions can be used with other asynchronous patterns, such as promises and generators.



Async functions with promises


Async functions can be used with promises by chaining a then block to the end of the async function call. This allows you to use async functions in conjunction with other asynchronous patterns, such as setTimeout or setInterval .

Here is an example of using an async function with a setTimeout function:

 async function delayedLog() { await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Hello, world!'); } delayedLog();

In this example, the delayedLog function uses an async function to wait for a promise returned by the setTimeout function to be resolved. This causes the function to wait for one second before logging "Hello, world!" to the console.


Async functions with generators


Async functions can also be used with generators to create asynchronous iterators. This allows you to use async functions to create sequences of asynchronous tasks that can be iterated over using a for-of loop.

Here is an example of using an async function with a generator:

 async function* getData() { yield await fetch('https://api.example.com/data1'); yield await fetch('https://api.example.com/data2'); yield await fetch('https://api.example.com/data3'); } (async function() { for await (const data of getData()) { console.log(await data.json()); } })();

In this example, the getData function is an async generator that yields a series of promises returned by the fetch function. The for-await-of loop is used to iterate over the asynchronous generator, waiting for each promise to be resolved before moving on to the next iteration.


Conclusion

Async functions are a powerful new feature in JavaScript that makes it easy to write asynchronous code that is easy to read and debug. They are built on top of promises and generators, and they allow you to use the await keyword to wait for promises to be resolved. Async functions can be used with other asynchronous patterns, such as promises and generators, to create complex asynchronous programs.