Using javascript's Symbol.asyncIterator with for await of loop

javascript tutorial
javascript w3schools
jquery
javascript examples
learn javascript
javascript function
javascript hello world
html

I am trying to understand javascript's Symbol.asyncIterator and for await of. I wrote some simple code and it throws an error saying:

    TypeError: undefined is not a function

on the line which tries to use for await (let x of a).

I could not understand the reason for it.

let a = {}


function test() {
        for(let i=0; i < 10; i++) {
                if(i > 5) {
                        return Promise.resolve(`Greater than 5: (${i})`)
                }else {
                        return Promise.resolve(`Less than 5: (${i})`)
                }
        }
}

a[Symbol.asyncIterator] = test;


async function main() {
        for await (let x of a) { // LINE THAT THROWS AN ERROR
                console.log(x)
        }
}


main()
        .then(r => console.log(r))
        .catch(err => console.log(err))

I create an empty object a and insert a key Symbol.asyncIterator on the same object and assign it a function named test that returns a Promise. Then I use for await of loop to iterate over all the values that the function would return.

What am I doing incorrectly?

PS: I am on the Node version 10.13.0 and on the latest version of Chrome

To be a valid asyncIterator, your test function must return an object with a next method that returns a promise of a result object with value and done properties. (Technically, value is optional if its value would be undefined and done is optional if its value would be false, but...)

You can do that in a few ways:

  1. Completely manually (awkward, particularly if you want the right prototype)
  2. Half-manually (slightly less awkward, but still awkward to get the right prototype)
  3. Using an async generator function (simplest)

You can do it completely manually (this doesn't try to get the right prototype):

function test() {
    let i = -1;
    return {
        next() {
            ++i;
            if (i >= 10) {
                return Promise.resolve({
                    value: undefined,
                    done: true
                });
            }
            return Promise.resolve({
                value: i > 5 ? `Greater than 5: (${i})` : `Less than 5: (${i})`,
                done: false
            });
        }
    };
}

let a = {
    [Symbol.asyncIterator]: test
};

async function main() {
    for await (let x of a) {
        console.log(x)
    }
}

main()
    .then(r => console.log(r))
    .catch(err => console.log(err))

JavaScript Examples, Using innerHTML. To access an HTML element, JavaScript can use the document.getElementById(id) method. The id attribute defines the� The heading text changed to Hello world! using JavaScript. You did this by using a function called querySelector() to grab a reference to your heading, and then store it in a variable called myHeading. This is similar to what we did using CSS selectors. When you want to do something to an element, you need to select it first.

The test function must not return a promise, but an Iterator (an object with a next() ) method, that method then has to return a Promise (which makes it an async iterator) and that Promise has to resolve to an object containing a value and a done key:

function test() {
   return {
     next() {
       return Promise.resolve({ value: "test", done: false });
     }
   };
}

Now while that works, it is not that useful yet. You could however create the same behaviour with an async generator function:

  async function* test() {
    await Promise.resolve();
    yield "test";
  }

Or in your case:

async function* test() {
  for(let i = 0; i < 10; i++) {
    if(i > 5) {
      await Promise.resolve();
      yield `Greater than 5: (${i})`;
    }else {
      await Promise.resolve();
      yield `Less than 5: (${i})`;
    }
  }
}

JavaScript Tutorial, JavaScript is a programming language that adds interactivity to your website. Learn JavaScript or free with our easy to use input output machine. JavaScript.com is a resource for the JavaScript community. You will find resources and examples

You should make test an async generator function instead, and yield instead of return:

let a = {}


async function* test() {
  for(let i=0; i < 10; i++) {
    if(i > 5) {
      yield Promise.resolve(`Greater than 5: (${i})`)
    }else {
      yield Promise.resolve(`Less than 5: (${i})`)
    }
  }
}

a[Symbol.asyncIterator] = test;


async function main() {
  for await (let x of a) {
    console.log(x)
  }
}


main()
  .then(r => console.log(r))
  .catch(err => console.log(err))

JavaScript Output, A very common use of JavaScript is to dynamically modify HTML and CSS to update a user interface, via the Document Object Model API (as� JavaScript looks up an unqualified name by searching a scope chain associated with the execution context of the script or function containing that unqualified name. The 'with' statement adds the given object to the head of this scope chain during the evaluation of its statement body.

JavaScript basics, The concepts covered in these lessons lay the foundation for using JavaScript in any environment. Up Next: After learning JavaScript basics (up through the� Well organized and easy to understand Web building tutorials with lots of examples of how to use HTML, CSS, JavaScript, SQL, PHP, Python, Bootstrap, Java and XML.

What is JavaScript?, The things we tell software using a programming language could be to make a webpage look a certain way, or to make an object on the page� Well organized and easy to understand Web building tutorials with lots of examples of how to use HTML, CSS, JavaScript, SQL, PHP, Python, Bootstrap, Java and XML.

Learn JavaScript, Let's go through the entire video and learn using JavaScript in HTML Page. For more updates Duration: 8:56 Posted: 13 Jul 2017 JavaScript Display Possibilities. JavaScript can "display" data in different ways: Writing into an HTML element, using innerHTML. Writing into the HTML output using document.write(). Writing into an alert box, using window.alert(). Writing into the browser console, using console.log().

Comments
  • You need to use generators and yield instead of return. should work then
  • For the last part ... wouldn't [Symbol.asyncIterator]() { return this; } be enough ?
  • @JonasWilms - Yes...unless your project enhances async iterators by adding features to the prototype. :-) Since TC39 made it such a pain to get the prototype, I don't think anyone will ever actually expect enhancing the prototype to work with anything other than native async iterators and ones they've written themselves ensuring the prototype. So in practice, almost all the time, yes.
  • When using with yield, does it implicitly return value and done?
  • @SuhailGupta - Yes, the generator / async generator itself provides the wrapper result object, which is part of why generators are so handy for creating iterators. With yield, the result object will have the yielded value and done: false. With return (in a generator function), the result object will have the yielded value and done: true. (Similarly: for-await-of and for-of consume the result object, using its value and next under the covers.)
  • Could you explain the purpose of await Promise.resolve();
  • @suhail await halts the iterator until Promise.resolve() or any other asynchronous action wrapped in a promise, is done.
  • @SuhailGupta - E.g., in the above it just demonstrates using the power of async/await in the async generator.
  • Thats not an async iterator though.
  • You've written an async generator. There's no reason to use Promise.resolve if you're going to use an async generator to do this, yield the values directly; it wraps them for you. But I thought the point from the OP was that they wanted to understand these under the covers.
  • @T.J.Crowder I thought the Promise.resolve was there just to imitate something asynchronous being done - in non-example code, there'd probably be an await in there, so the async function would make more sense, right?
  • @CertainPerformance - There's no reason to yield await somePromise in an async generator, just like there's no reason to return await somePromise in an async function; in both cases, the value is automatically awaited (effectively). Until a recent spec change, adding the await just delayed things by an extra async tick. (With the latest spec change, done just after ES2019, as long as what you await is a native promise, that extra tick gets optimized away.)
  • @JonasWilms - Well, nobody's perfect. :-) It wouldn't be surprising if the text of the proposal got slightly out of date as things progressed. For details on the changes to make yield await promise and return await promise perform better: v8.dev/blog/fast-async Also see Mathias Bynens' replies to my tweet here. :-)