Search code examples
javascriptnode.jsseleniumasync-awaityield-return

Can Asynchronous Generators be called using a pass through / helper function in Node.js?


What I can do

I figured out that the asynchronous generator pattern is fairly new to JavaScript and only available in Node.js as of version 10. So after I did all that, I can now use the following function to yield multiple similar elements in a web page using Selenium, which uses async/await for many of its functions:

formElements = async function*(containerCss, childCss, defaultCount) {
    let countCSS = containerCss + " " + childCss;

    let numElements = await countCss(countCSS) || defaultCount;

    for (let cIndex = 1; cIndex <= numElements; cIndex++) {
        let elementCss = containerCss + ":nth-child(" + cIndex + ") " + childCss;

        yield await elmCSS(elementCss);
    }
}

This function works fine as an asynchronous generator using this call:

for await (const button of formElements("button-container-css", "button-css", 6))
{
    await clickOn(button);

    //other async stuff...
}

What I can't figure out

But what I want to do is take those specific parameters out of the main function and have a helper function (formButtons()) provide them instead, which I think should look like this (?):

formButtons = async function*() {
    yield await formElements("button-container-css", "button-css", 6);
}

so my main function can be a LITTLE cleaner:

for await (const button of formButtons())
{
    await clickOn(button);
    //other async stuff...
}

But it literally skips everything once it hits the yield in formButtons(), returning null and causing errors. I think the error must either be in my formButtons() function or else it's some unfortunate artifact of not using the stable release of Node, but I'm hopeful it's something I can fix

Is there an error or a workaround for this?


Solution

  • With the caveat that I've used generators only a few times in toy applications, the situation you've got is basically a delegation from one generator to another one. Since what you want to do is "pass through" control to the sub-generator completely, you can use yield* instead of yield. It basically means, "yield everything you can get from this generator". So:

    formButtons = async function*() {
        yield* await formElements("button-container-css", "button-css", 6);
    }