Доброго дня!
Есть следующий код, который асинхронно перебирает значения в объекте, который мы самостоятельно сделали итерируемым:
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()...