javascriptecmascript-2018

Wait for an async operation inside asyncIterator generator function


I have this Queue class (not the real implementation, but it exemplifies my point):

class Queue {
    constructor() {
        this._arr = [];
    }

    async push(elem) {
        this._arr.push(elem);
    }

    async pop() {
        return this._arr.pop();
    }

    *[Symbol.asyncIterator]() {
        do {
            let res = await this.pop(); // here is the problem
            if (res) yield res;
        } while (res);
    }
}

It's just a wrapper for a Javascript Array with the exception that its methods return a Promise.

What I want to do is to yield conditionally based on the return value of the pop() method, which I cannot do because await is not a valid operation inside an asyncIterator generator function.

I thought about having a flag which is set in the previous iteration:

*[Symbol.asyncIterator]() {
    let continue = true;
    do {
        yield this.pop().then(v => {
            if (!v) continue = false;
            return v
        });
    } while (continue);
}

But this would still return a undefined value in the last execution of pop().

I could handle this in the calling code by checking for a undefined value as a signal of the end of the iteration, but I was wondering if there was a better approach at tackling this problem.


Solution

  • You can use an async generator function (MDN docs missing, but see e.g. this article) for the implementation of the [Symbol.asyncIterator]() method:

    async *[Symbol.asyncIterator]() { /*
    ^^^^^ */
        while (this._arr.length) {
            yield await this.pop(); // no longer a problem
        }
    }