简体   繁体   中英

What is the best practice to create an async iterator? Should I use an async generator function or rather use Symbol.asyncIterator?

This code works as expected:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getAsyncData() {
    await sleep(1000);  // simulate database/network delay...
    return [1, 2, 3, 4, 5];  // ...then return some data
}

const asyncIterable = (async function* filterAsyncData() {
    const items = await getAsyncData();

    for (const item of items) {
        yield item;
    }
})();

const asyncIterable2 = {
    [Symbol.asyncIterator]() {
        return {
            values: null,
            idx: 0,
            async next() {
                if (this.values === null) {
                    this.values = await getAsyncData();
                }

                if (this.idx < this.values.length) {
                    this.idx = this.idx + 1;
                    return Promise.resolve({ value: this.values[this.idx - 1], done: false });
                }

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

async function main() {
    for await (const filteredItem of asyncIterable) {
        console.log(filteredItem);
    }
}

main()

It does not mather if I use asyncIterable or asyncIterable2 in the main function, I always get the same result. What is the best practice to define my iterable? Are there any guidelines about which option is preferred? Why?

It's the same as for synchronous iterators : generator functions are much easier to write, and easier to get correct, than implementing the iterator object manually. Do this only if you need some non-standard behaviour that cannot be achieved otherwise. With asynchronous generator functions specifically, you even get the proper queueing of next calls for free, which is a real headache to get right (your asyncIterable2 fails this 1 ).

The most common implementation of iterables is to make the Symbol.asyncIterator method an async generator method:

const asyncIterable = {
    async *[Symbol.asyncIterator]() {
         yield* await getAsyncData();
    },
};

1: const it = asyncIterable2[Symbol.asyncIterator](); it.next(); it.next() const it = asyncIterable2[Symbol.asyncIterator](); it.next(); it.next() const it = asyncIterable2[Symbol.asyncIterator](); it.next(); it.next() - without any await s in between - will call getAsyncData twice, because this.values == null in both calls

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM