Reputation: 33
Let's say we have two async iterators,
const asyncIterable1 = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
const asyncIterable2 = {
[Symbol.asyncIterator]() {
return {
i: 3,
next() {
if (this.i < 5) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
Now, is there a way to combine these two iterators into one iterator that would return a sequence of 0,1,2 and then 3,4?
Upvotes: 3
Views: 1491
Reputation: 350961
Another alternative is to use the iterator helper method flatMap
, introduced with ECMAScript 2025:
const iterator1 = (function* () { yield 1; yield 2; })();
const iterator2 = (function* () { yield 3; yield 4; })();
const iterator1then2 = [iterator1, iterator2].values().flatMap(Object);
console.log(...iterator1then2);
NB: Object
is used here as an identity function.
Upvotes: 0
Reputation: 138477
Yeah, I'd use yield*
for that:
const combine = (a, b) => (function* () { yield* a; yield* b; })();
const iterator = combine(
(function* () { yield 1; yield 2; })(),
(function* () { yield 3; yield 4; })()
);
console.log(iterator.next(), iterator.next(), iterator.next(), iterator.next(), iterator.next());
This works analogously for async iterators. You'll loose the return value ("the done yield") of the first iterator though. You could capture it however (the value yield*
evaluates to).
For sure if you're among the people that like to reinvent wheels, you can also implement such functionality "by hand" without generator functions:
function combine(...iterators) {
let pos = 0, iterator = iterators[pos];
return {
next() {
let result = { done: true };
do {
result = iterator.next();
if(!result.done) break;
iterator = iterators[ pos++ ];
} while(iterator)
return result;
}
};
}
const iterator = combine(
(function* () { yield 1; yield 2; })(),
(function* () { yield 3; yield 4; })()
);
console.log(iterator.next(), iterator.next(), iterator.next(), iterator.next(), iterator.next());
Upvotes: 4
Reputation: 8170
In addition to Jonas' great answer, we could generalize a bit further and combine an arbitrary number of iterators:
let combine = function*(...iterators) {
for (let it of iterators) yield* it;
};
Upvotes: 7