This functionality was added in ES9 (ECMAScript 2018). A distinctive aspect of async generator functions in JavaScript is their ability to utilize both await and yield keywords. Unlike a conventional Promise or a synchronous iterator, it yields an async iterator.
Async generator functions are different from both async functions and generator functions in JavaScript. This type of function merges the capabilities of async functions with those of generator functions. The next method of an async iterator consistently returns a promise.
The primary distinction between an async generator and a traditional generator lies in the fact that the next method of an async generator yields a Promise, allowing the use of await within its implementation. In contrast, a conventional generator's next method produces an Iterator result synchronously and does not permit the use of await within its structure.
Syntax:
The syntax presented below illustrates an asynchronous generator within a JavaScript function.
async function* function_name() {
...
}
Explanation:
async: The async keyword indicates that the function is asynchronous in nature. Regardless of whether a non-promise value is returned, the function will invariably yield a promise value.
function : The presence of the asterisk () symbol following a function indicates that the function is a generator.
function_name : Give a function name.
Uses of JavaScript Async Generator Functions:
The scenarios in which JavaScript Async Generator Functions can be utilized include the following:
Beneficial for Handling Large Files in Segments: Asynchronous generator functions prove to be extremely effective for managing substantial files. An async generator has the capability to yield segments of data as they are ready, enabling ongoing processing while minimizing memory usage.
Beneficial for Overseeing Complicated Asynchronous Workflows: When aiming to orchestrate a sequence of asynchronous tasks that need to execute in a specific order, utilizing an async generator offers a more organized and manageable approach to regulate the workflow. It enables us to pause or continue execution at specified moments within the process.
Beneficial for Managing Pagination API Calls: When interacting with APIs that provide paginated responses, which may not consistently have the same number of items per page, asynchronous generators offer a more straightforward approach to retrieve and process each page sequentially. This allows us to implement lazy-loading for the data effectively.
Real-time Data Streams: This feature proves highly beneficial for applications that utilize live data sourced from WebSockets or event streams. In such situations, asynchronous generators can be employed to traverse the live data. This approach ensures that our application remains responsive to incoming events, as they occur without obstructing the main thread.
Examples of JavaScript Async Generator Functions:
The subsequent examples demonstrate various techniques and functionalities utilized in JavaScript.
Example 1:
In this illustration, we employ the throw keyword to generate an error using an error object. The function will incorporate a custom error message supplied by the user, after which we will utilize a catch statement to handle the error appropriately.
Example
console.log("JavaScript async generator");
async function* generator_function() {
throw new Error("Error thrown in the "
+ " JavaScript async generator function....!!!");
}
let iterator_var = generator_function();
iterator_var
.next()
.then((results) => console.log(results.value))
.catch((errors) => console.log(errors.message));
Output:
JavaScript async generator
Error thrown in the JavaScript async generator function....!!!
Explanation :
In the preceding example, we establish an asynchronous generator utilizing the syntax async function *. An error is raised instantly when the generator is initially executed. We introduce a variable referred to as iterator_var. This invokes the generator, resulting in the creation of an async iterator object. However, the function body does not execute immediately until the .next method is invoked. In this instance, the .next method is called to run the generator. Since an error occurs at the outset, the promise returned by .next is rejected, and the .catch method captures that error and provides the corresponding error message.
Example 2
In this illustration, we will combine the async generator with the yield keyword. By employing a catch statement, we will initially define a promise that is rejected and contains the error message within the reject method.
Example
console.log("JavaScript async generator");
async function* generator_fun() {
yield new Promise((resolve, reject) => {
reject("Result thrown in the "
+ " JavaScript async generator function using yield method....!!!");
});
}
let iterator_var = generator_fun();
iterator_var
.next()
.then((result) => console.log(result.value))
.catch((error) => console.log(error));
Output:
JavaScript async generator
Result thrown in the JavaScript async generator function using the yield method....!!!
Explanation:
In the preceding example, we initially displayed a message indicating "JavaScript async generator." Next, we defined an asynchronous generator function using the syntax function* generatorfun. Upon invoking this function, it produces an asynchronous iterator, which includes the methods .next, .throw, and .return, all of which return new promises. In this context, the yield new Promise(…) is employed to yield a promise that promptly rejects with an error message. The variable let iteratorvar is employed to instantiate an iterator derived from the async generator. Subsequently, iterator_var.next is called to initiate the generator, retrieving the first yield, and then we printed out the rejected promise.
Example 3
In this illustration, the asynchronous generator function demonstrates the application of both the yield and await keywords.
Example
console.log("JavaScript async generator");
'use strict';
async function* function_data() {
await new Promise(resolve => setTimeout(resolve, 100));
yield 'Hello Student';
console.log('Welcome to Development World');
}
const asyncfunction_data = function_data();
(async () => {
for await(const values of asyncfunction_data) {
console.log(values); // print the input value
}
})();
Output:
JavaScript async generator
Hello Student
Welcome to Development World
Explanation:
In the preceding example, we print a message regarding the JavaScript async generator. In this context, we utilize "use strict" to activate strict mode, which helps us identify common programming errors. The function async function* functiondata defines an asynchronous generator function. Upon invocation, it provides an asynchronous iterator. The statement await new Promise awaits for 100 milliseconds, effectively simulating an asynchronous delay. We specify yield 'Hello Student', which yields 'Hello Student' as the initial output after the specified delay. The line console.log('Welcome to Development World') executes once the value has been consumed from the generator, following the completion of the yield. The declaration const asyncfunctiondata = function_data calls the generator function and acquires an async iterator. Additionally, we define (async => { … } ), which represents an immediately invoked function expression (IIFE), enabling the use of await and for await within its scope. The for await construct iterates through the async iterator returned by the generator. The message "JavaScript async generator" is displayed immediately. After a delay of 100ms, 'Hello Student' is yielded and printed within the loop. Finally, "Welcome to Development World" is displayed after the generator has finished executing.
Example 4
The illustration demonstrates the iterator's value through both the operator and the function. This iterator reveals data ranging from the initial to the final values produced by the asynchronous generator function.
Example
console.log("JavaScript async generator");
async function* asyncFunction(start, end) {
for(let y = start; y <= end; y++) {
yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve(y);
}, 100);
});
}
}
(async () => {
let sequence_var = asyncFunction(2, 5);
for await(let num_value of sequence_var) {
console.log(num_value);
}
})();
Output:
JavaScript async generator
2
3
4
5
Explanation:
In the previous illustration, we are demonstrating the output of a message utilizing a JavaScript asynchronous generator. We have established an asynchronous generator function referred to as asyncFunction. This function accepts two arguments, named start and end. The use of the async function * syntax indicates that this function is capable of yielding values in an asynchronous manner. Within the function, there exists a for loop that iterates from the start number to the end number. At this point, yield new Promise produces a promise for the caller. The setTimeout function introduces a delay of 100 milliseconds before the promise is resolved. Once the 100ms delay elapses, the promise is fulfilled with the current number y (the async => {} initiates an asynchronous Immediately Invoked Function Expression). The variable let sequencevar = asyncFunction(2, 5) is used to invoke asyncFunction with start set to 2 and end set to 5. The construct for await (let numvalue of sequencevar) employs the await…of loop to asynchronously consume values from the generator. It pauses until each promise is resolved before proceeding to the next iteration. Finally, we utilize console.log(numvalue) to display the output.
Conclusion
JavaScript async generators serve the purpose of managing various asynchronous data streams. They are utilized within promises, iterators, and other methodologies to simplify complex functionality effectively.
- What is the primary distinction between a generator and a standard function in JavaScript?
In JavaScript, the primary distinction between a generator and a conventional function lies in the fact that a generator can be paused and resumed, whereas a regular function cannot. When we invoke a regular function, it executes in its entirety until it finishes. Conversely, a generator yields an iterator object that can be employed to produce a sequence of values incrementally.
- What is the main purpose of the yield* keyword in generators?
The primary purpose of the yield* keyword within generators is to facilitate the iteration over another generator or an iterable object. It enables a generator to yield values from a secondary generator or iterable, thereby streamlining the iteration process.
- How do you define an Async Generator function?
In JavaScript, an asynchronous generator function is defined by combining the async and function* keywords.
async function* myAsyncGenerator() {
//
}
- What is the return value of an Async Generator function?
An asynchronous generator function yields an AsyncIterator object. This object includes a next method that provides a Promise, which resolves to an object containing value and done properties, akin to those found in a standard Iterator.
- What is the process for retrieving values from an Async Generator function?
Values produced by an async generator function are retrieved utilizing the for-await-of loop. This loop traverses the asynchronous values that the generator yields, halting execution until each Promise is fulfilled.
- What transpires if an error arises within an Async Generator function? How can one manage it?
Errors that occur inside an async generator function can be handled using the conventional try…catch statements within the generator itself. When utilizing a for-await-of loop to iterate over a generator, any errors that arise will be passed along and can be captured by a try…catch block that encompasses the for-await-of loop.