Почему измененный код асинхронного итератора не работает?

Доброго дня!

Есть следующий код, который асинхронно перебирает значения в объекте, который мы самостоятельно сделали итерируемым:

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
  }

})()

Пояснения к коду из учебника:
Чтобы сделать объект асинхронно итерируемым, он должен иметь метод Symbol.asyncIterator (1).
Этот метод должен возвращать объект с методом next(), который в свою очередь возвращает промис (2).
Метод next() не обязательно должен быть async, он может быть обычным методом, возвращающим промис, но async позволяет использовать await, так что это удобно. Здесь мы просто делаем паузу на одну секунду (3).
Для итерации мы используем for await (let value of range) (4), добавляя «await» после «for». Он вызовет range[Symbol.asyncIterator]() один раз, а затем его метод next() для получения значений.

Решил я, значит, немножко переписать итератор, а вернее его метод next(), чтобы он был не async, но возвращал промис. Сделал я это так:

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 })
        	}
        })

      }
    }
  }
}

В итоге такой код отказывается работать.
Что я сделал не так?

Как указано выше, метод next() не обязательно должен быть async. Главное, чтобы он возвращал промис. Я обернул возвращаемый объект { done: <...>, value: <...>} в промис, но это не помогло.

И еще вопрос: почему мы не можем сделать так?

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

Т.е. просто вернуть значения? Ведь .then у нас по идее возвращает промис, а это и надо для метода next()...
  • Вопрос задан
  • 62 просмотра
Решения вопроса 1
Aetae
@Aetae Куратор тега JavaScript
Тлен
Ты собственно не вернул промис. Нет return из метода next.)
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
dedavarera
@dedavarera Автор вопроса
Все, коллеги, разобрался. Я не вернул сам промис.
Вот такой код работает:

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()


И отвечая на доп. вопрос: все же так можно сделать, и это видно из кода выше. Метод then возвращает промис, но с нюансами: если обработчик handler в then(handler) заканчивается возвратом некоторого значения, то возвращенный then промис выполняется с этим значением.

В моем первом случае обработчик возвращал новый промис, и возвращаемый then промис завершался с результатом этого нового промиса, что, собственно, делать было не обязательно и являлось лишним шагом в коде.

Итого: завершенный с некоторым результатом промис возвращается из then, но его надо вернуть из next().
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы