I'm creating an array that iterates asynchronously (for fun). This works fine:
class AsyncArray extends Array {
constructor() {
super();
this.x = 0;
}
[Symbol.iterator]() {
return {
next: () => {
let promise = new Promise(resolve => setTimeout(
() => resolve(this.x++), 1000)
);
return {done: this.x >= this.length, value: promise};
}
};
}
}
async () => {
for (let x of AsyncArray.of(1, 2, 3)) {
let value = await x;
console.log(value);
}
}();
However, this prints out 0...1...2
because I'm keeping track of the current counter on my own and initializing it to x
.
Is there any way to get the current iterator value internal to the Array? I would also need to be able to properly determine the done
value.
I guess you don't want the counter internal to your array, but rather to your iterator. Use a local variable in the method for that:
[Symbol.iterator]() {
var x = 0;
return {
next: () => {
let promise = new Promise(resolve =>
setTimeout(() => resolve(this[x++]), 1000)
);
return {done: x >= this.length, value: promise};
}
};
}
The easiest way to write iterators though is by using a generator function:
[Symbol.iterator]*() {
for (var x = 0; x < this.length; x++)
yield new Promise(resolve =>
setTimeout(() => resolve(this[x]), 1000)
);
}
That will take care of the correct done
value as well (and won't "return
" a promise that resolves with undefined
).
An alternative that would completely avoid tracking state in a local variable or instance property would be to make use of the standard array iterator:
[Symbol.iterator]() {
var vals = super[Symbol.iterator]();
var it = Object.create(Object.getPrototypeOf(vals)); // an array iterator
it.next = () => {
var res = vals.next();
if (!res.done)
return {done: false, value: new Promise(resolve =>
setTimeout(() => resolve(res.value), 1000)
)};
return res;
};
return it;
}