M
M
Maxim Belousov2021-07-09 15:12:21
JavaScript
Maxim Belousov, 2021-07-09 15:12:21

Why is the modified async iterator code not working?

Good day!

There is the following code that asynchronously iterates over the values ​​in an object that we made iterable ourselves:

let range = {
  from: 1,
  to: 5,

  // for await..of вызывает этот метод один раз в самом начале
  [Symbol.asyncIterator]() { // (1)
    // ...возвращает объект-итератор:
    // далее for await..of работает только с этим объектом,
    // запрашивая у него следующие значения вызовом next()
    return {
      current: this.from,
      last: this.to,

      // next() вызывается на каждой итерации цикла for await..of
      async next() { // (2)
        // должен возвращать значение как объект {done:.., value :...}
        // (автоматически оборачивается в промис с помощью async)

        // можно использовать await внутри для асинхронности:
        await new Promise(resolve => setTimeout(resolve, 1000)); // (3)

        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

(async () => {

  for await (let value of range) { // (4)
    alert(value); // 1,2,3,4,5
  }

})()

Explanations for the code from the tutorial:
To make an object asynchronously iterable, it must have a Symbol.asyncIterator(1) method.
This method must return an object with a next() method, which in turn returns a promise (2).
The next() method doesn't have to be async, it can be a regular promise-returning method, but async allows you to use await, so it's convenient. Here we just pause for one second (3).
For iteration, we use for await (let value of range) (4), adding "await" after "for". It will call range[Symbol.asyncIterator]() once and then its next() method to get the values.

I decided, therefore, to slightly rewrite the iterator, or rather its next () method, so that it was not async, but returned a promise. I did it like this:

let range = {
  from: 1,
  to: 5,

  [Symbol.asyncIterator]() {
    return {
      current: this.from,
      last: this.to,

      next() {

      	new Promise(resolve => setTimeout(resolve, 2000))
        .then(() => {
          if (this.current <= this.last) {
          	return Promise.resolve({ done: false, value: this.current++ })
        	} else {
          	return Promise.resolve({ done: true })
        	}
        })

      }
    }
  }
}

As a result, such code refuses to work.
What have I done wrong?

As stated above, the next() method does not have to be async. The main thing is that it returns a promise. I wrapped the returned object { done: <...>, value: <...>} in a promise, but that didn't help.

And another question: why can't we do this?

.then(() => {
    if (this.current <= this.last) {
          	return { done: false, value: this.current++ }
        	} else {
          	return { done: true }
        	}
        }

Those. just return values? After all, .then, in theory, returns a promise, and this is what is needed for the next () method ...

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Aetae, 2021-07-09
@dedavarera

You didn't actually return the promise. None returnfrom method next.)

M
Maxim Belousov, 2021-07-09
@dedavarera

Everything, colleagues, understood. I didn't return the promise itself.
Here is the code that works:

let range = {
  from: 1,
  to: 5,

  [Symbol.asyncIterator]() {
    return {
      current: this.from,
      last: this.to,

      next() {

      	return new Promise(resolve => setTimeout(resolve, 2000))
        .then(() => {
          if (this.current <= this.last) {
          	return { done: false, value: this.current++ }
        	} else {
          	return { done: true }
        	}
        })

      }
    }
  }
}

let count = async function() {
  for await (let value of range) {
    console.log(value);
  }
}

count()

And answering additional question: it is still possible to do this, and this can be seen from the code above. The then method returns a promise, but with nuances: if the handler in then(handler) ends up returning some value, then the promise returned by then is executed with that value.
In my first case, the handler returned a new promise, and the promise returned by then ended with the result of this new promise, which, in fact, was not necessary and was an extra step in the code.
Bottom line: a promise completed with some result is returned from then, but it must be returned from next().

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question