shelving
Version:
Toolkit for using data in JavaScript.
78 lines (77 loc) • 2.65 kB
JavaScript
import { awaitDispose } from "../util/dispose.js";
import { Starter } from "../util/start.js";
import { ThroughSequence } from "./ThroughSequence.js";
/** Sequence of values that calls a `StartCallback` when it has iterators that are iterating, and calls the corresponding `StopCallback` when all iterators have finished. */
export class LazySequence extends ThroughSequence {
_iterators = new Set(); // Keep track of the iterators that are iterating.
_starter;
/** Get the number of iterators currently registered. */
get iterators() {
return this._iterators.size;
}
constructor(source, start) {
super(source);
this._starter = new Starter(start);
}
// Implement `AsyncIterable`
[Symbol.asyncIterator]() {
// Just returns a new iterator.
return new LazyIterator(this);
}
// Implement `AsyncDisposable`
async [Symbol.asyncDispose]() {
await awaitDispose(this._starter, // Stop the starter.
super[Symbol.asyncDispose]());
}
/**
* An iterator started iterating.
* - Add this iterator to the register.
* - Start the starter if this is the first iterator.
*/
start(iterator) {
const before = this._iterators.size;
this._iterators.add(iterator);
if (before === 0 && this._iterators.size === 1)
this._starter.start();
}
/**
* An iterator stopped iterating.
* - Add this iterator to the register
* - Stop the starter if this is the last iterator.
*/
stop(iterator) {
const before = this._iterators.size;
this._iterators.delete(iterator);
if (before === 1 && this._iterators.size === 0)
this._starter.stop();
}
}
/** Internal `Iterator` that allows the lazy sequence to work. */
class LazyIterator {
_sequence;
constructor(sequence) {
this._sequence = sequence;
}
next(value) {
this._sequence.start(this); // Iterator has started iterating.
return this._result(this._sequence.next(value));
}
return(value) {
return this._result(this._sequence.return(value));
}
throw(reason) {
return this._result(this._sequence.throw(reason));
}
async _result(result) {
try {
const r = await result;
if (r.done)
this._sequence.stop(this); // Iterator has stopped iterating because `done: true` was returned.
return r;
}
catch (reason) {
this._sequence.stop(this); // Iterator has stopped iterating because it threw.
throw reason;
}
}
}