Asynchronous iterators and generators in JavaScript. Code ex... open

Asynchronous iterators and generators in JavaScript. Code examples

Approved. Code works!
This is exactly the working code that is verified by the moderator or site administrators
Tested: ES6

Asynchronous iterators combine the capabilities of iterators and the .

Asynchronous iterators are primarily intended for accessing data sources that use the asynchronous API. It can be some data that is loaded in parts, for example, over a network, from a file system, or from a database.

An asynchronous iterator is similar to a normal synchronous iterator, except that its next() method returns an object .

And from the Promise, in turn, the object { value, done } is returned.

To get data using asynchronous iterators, a for-await-of loop is used:

    for await (variable of iterable) {
        // actions
    }

iterable – asynchronous or synchronous data source (array, Map, Set…)
!this form of loop can only be used in functions defined with an async statement.

Consider the simplest example, where an ordinary array acts as a data source:

    const array = ["Alexander", "Timote", "Andrey"];

    async function readData() {
        for await (const item of array) {
            console.log(item);
        }

    }
    readData();
// Alexander
// Timote
// Andrey

Here in the loop iterates over the dataSource array.
Looping through a data source (in this case, an array) with the [Symbol.asyncIterator]() method implicitly creates an asynchronous iterator.

And with each access to the next element in this data source, a Promise object is implicitly returned from the iterator, from which we get the current element of the array.

Creating an asynchronous iterator

In the example above, the asynchronous iterator was created implicitly. But we can also define it explicitly. For example, let’s define an asynchronous iterator that returns the elements of an array:

    const generatePerson = {

        [Symbol.asyncIterator]() {
            return {
                index: 0,
                people: ["Alexander", "Timote", "Andrey"],
                next() {
                    if (this.index < this.people.length) {
                        return Promise.resolve({ value: this.people[this.index++], done: false });
                    }

                    return Promise.resolve({ done: true });
                }
            };
        }

    };

So, the generatePerson object is defined here, in which only one method is implemented – [Symbol.asyncIterator](), which, in fact, represents an asynchronous iterator.
The implementation of an asynchronous iterator (as in the case of a synchronous iterator) allows you to make a generatePerson object sorted.

[Symbol.asyncIterator]() which returns an object. The returned iterator object has a next() method that returns a Promise object.

If all elements of the array have already been received, then return the Promise with the object { done: true }:

    return Promise.resolve({ done: true });

Getting data from an asynchronous iterator

As with a regular iterator, we can refer to the asynchronous iterator itself:

    generatePerson[Symbol.asyncIterator]();

And explicitly call its next() method:

    generatePerson[Symbol.asyncIterator]().next(); // Promise

This method returns a Promise, on which you can call the then() method and process its value:

    generatePerson[Symbol.asyncIterator]()
        .next()
        .then((data) => console.log(data));    // {value: "Alexander", done: false}

The object obtained from the promise represents the {value, done} object, from which the actual value can be obtained through the value property:

    generatePerson[Symbol.asyncIterator]()
        .next()
        .then((data) => console.log(data.value));  //Alexander

Since the next() method returns a Promise, we can use the await operator to get values:

    async function printPeople() {
        const peopleIterator = generatePerson[Symbol.asyncIterator]();

        while (!(personData = await peopleIterator.next()).done) {
            console.log(personData.value);
        }
    }
    printPeople();

Iterating with for-await-of:

    const generatePerson = {
        [Symbol.asyncIterator]() {
            return {
                index: 0,
                people: ["Tom", "Sam", "Bob"],
                next() {
                    if (this.index < this.people.length) {
                        return Promise.resolve({ value: this.people[this.index++], done: false });
                    }
                    return Promise.resolve({ done: true });
                }
            };
        }
    };
    async function printPeople() {
        for await (const person of generatePerson) {
            console.log(person);
        }
    }
    printPeople();

Because the generate Person object implements the [Symbol.asyncIterator]() method, we can iterate over it with a for-await-of loop.

Accordingly, with each call in the loop, the next() method will return a promise with the next element from people array.

Another simple example is getting numbers:

    const generateNumber = {
        [Symbol.asyncIterator]() {//create an iterator
            return {
                current: 0,//set the initial value
                end: 10,//set the final value

                next() {//

                    if (this.current <= this.end) {//if the initial value is less than the final value
                        return Promise.resolve({ value: this.current++, done: false });//in the result of the promise, we return an object with the value and property done:false, which will indicate that the iterator should continue to iterate over the numbers
                    }
                    return Promise.resolve({ done: true });//when the value is greater than the final one, we return done: true and finish the iterator
                }
            };
        }
    };
    async function printNumbers() {//create a new asynchronous function
        for await (const n of generateNumber) {//iterate our asynchronous iterator
            console.log(n);
        }
    }
    printNumbers();//launch the asynchronous function

Asynchronous generators

Asynchronous iterators open the way for us to create asynchronous generators.

Asynchronous generators allow us to use the await operator and receive and return data in an asynchronous manner.

To define an asynchronous generator, the generator function is preceded by the async statement

    async function* generatorName() {

        yield returned_value;
    }

Consider the simplest generator:

    async function* generatorNumber() {
        yield 5;
    }

Here, an asynchronous generator generatePersonAsync is defined, in which a single value is returned – the number 5.

A feature of an asynchronous generator is that when its next() method is called, a Promise object is returned.

And the resulting Promise object, in turn, returns an object with two properties {value, done}, where value actually stores the returned value, and done indicates whether more data is available in the generator.

For example, take the above defined asynchronous generator:

    async function* generateNumber() {
        yield 5;
    }

    const personGenerator = generateNumber();
    generateNumber.next(); // Promise

Here, using the next() method, we get a Promise. Further, through the then() method, we can get an object from the promise:

    const newNumber = generateNumber();

    newNumber.next()
        .then(data => console.log(data));    // {value: "5", done: false}

Receive data:

    console.log(data.value);  // 5

Using the await operator from the generator’s next() method, we can get the data:

    async function* generatePersonAsync() {
        yield "Alexander";
        yield "Timote";
        yield "Sergey";
    }

    async function printPeopleAsync() {
        const personGenerator = generatePersonAsync();

        while (!(person = await personGenerator.next()).done) {
            console.log(person.value);
        }
    }

    printPeopleAsync();

Since an asynchronous generator represents an asynchronous iterator, the generator data can also be obtained through a loop for-await-of:

    async function* generatePersonAsync() {
        yield "Alexander";
        yield "Timote";
        yield "Sergey";
    }

    async function printPeopleAsync() {
        const personGenerator = generatePersonAsync();
        for await (person of personGenerator) {
            console.log(person);
        }
    }

    printPeopleAsync();
// Alexander
// Timote
// Sergey

The main advantage of asynchronous generators is that we can use the await operator in them and receive data from data sources that use the asynchronous API.

    async function* generatePersonAsync(people) {
        for (const person of people)
            yield await new Promise(resolve => setTimeout(() => resolve(person), 2000));
    }

    async function printPeopleAsync(people) {
        for await (const item of generatePersonAsync(people)) {
            console.log(item);
        }
    }

    printPeopleAsync(["Alexander", "Timote", "Sergey"]);
0

More

Leave a Reply

Your email address will not be published. Required fields are marked *

How many?: 22 + 22

lil-code© | 2022 - 2024
Go Top
Authorization
*
*
Registration
*
*
*
*
Password generation