UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

78 lines (77 loc) 2.65 kB
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; } } }