UNPKG

ix

Version:

The Interactive Extensions for JavaScript

138 lines (127 loc) 5.27 kB
import { AsyncIterableX } from '../asynciterablex.js'; import { IRefCountList, MaxRefCountList, RefCountList, } from '../../iterable/operators/_refcountlist.js'; import { create } from '../create.js'; import { OperatorAsyncFunction } from '../../interfaces.js'; import { throwIfAborted } from '../../aborterror.js'; /** @ignore */ export class MemoizeAsyncBuffer<T> extends AsyncIterableX<T> { protected _source: AsyncIterator<T>; protected _buffer: IRefCountList<T>; protected _shared: Promise<IteratorResult<T>> | null; protected _error: any; protected _stopped: boolean; constructor(source: AsyncIterator<T>, buffer: IRefCountList<T>) { super(); this._error = null; this._shared = null; this._stopped = false; this._source = source; this._buffer = buffer; } [Symbol.asyncIterator](signal?: AbortSignal) { throwIfAborted(signal); return this._getIterable(0); } protected async *_getIterable(offset = 0) { let i = offset - 1; let done: boolean | undefined = false; const buffer = this._buffer; try { do { if (++i < buffer.count) { yield buffer.get(i); continue; } if (this._stopped) { throw this._error; } if (this._shared === null) { this._shared = this._source.next().then((r) => { this._shared = null; if (!r.done) { buffer.push(r.value); } return r; }); } ({ done } = await this._shared.catch((e) => { this._error = e; this._stopped = true; throw e; })); if (!done) { yield buffer.get(i); } } while (!done); } finally { buffer.done(); } } } /** * Creates a buffer with a view over the source sequence, causing a specified number of iterators to obtain access * to all of the sequence's elements without causing multiple enumerations over the source. * @template TSource Source sequence element type. * @param {number} [readerCount] Number of iterators that can access the underlying buffer. * Once every iterator has obtained an element from the buffer, the element is removed from the buffer. * @returns {OperatorAsyncFunction<TSource, TSource>} Buffer enabling a specified number of iterators to retrieve all * elements from the shared source sequence, without duplicating source iteration side-effects. */ export function memoize<TSource>(readerCount?: number): OperatorAsyncFunction<TSource, TSource>; /** * Memoizes the source sequence within a selector function where a specified number of iterators can get access * to all of the sequence's elements without causing multiple iterations over the source. * * @template TSource Source sequence element type. * @template TResult Result sequence element type. * @param {number} [readerCount] Number of iterators that can access the underlying buffer. Once every * iterator has obtained an element from the buffer, the element is removed from the buffer. * @param {(value: AsyncIterableX<TSource>) => AsyncIterable<TResult>} [selector] Selector function with memoized access * to the source sequence for a specified number of iterators. * @returns {OperatorAsyncFunction<TSource, TResult>} Sequence resulting from applying the selector function to the * memoized view over the source sequence. */ export function memoize<TSource, TResult>( readerCount?: number, selector?: (value: AsyncIterable<TSource>) => AsyncIterable<TResult> ): OperatorAsyncFunction<TSource, TResult>; /** * Memoizes the source sequence within a selector function where a specified number of iterators can get access * to all of the sequence's elements without causing multiple iterations over the source. * * @template TSource Source sequence element type. * @template TResult Result sequence element type. * @param {number} [readerCount=-1] Number of iterators that can access the underlying buffer. Once every * iterator has obtained an element from the buffer, the element is removed from the buffer. * @param {(value: AsyncIterable<TSource>) => AsyncIterable<TResult>} [selector] Selector function with memoized access * to the source sequence for a specified number of iterators. * @returns {(OperatorAsyncFunction<TSource, TSource | TResult>)} Sequence resulting from applying the selector function to the * memoized view over the source sequence. */ export function memoize<TSource, TResult = TSource>( readerCount = -1, selector?: (value: AsyncIterable<TSource>) => AsyncIterable<TResult> ): OperatorAsyncFunction<TSource, TSource | TResult> { return function memoizeOperatorFunction( source: AsyncIterable<TSource> ): AsyncIterableX<TSource | TResult> { if (!selector) { return readerCount === -1 ? new MemoizeAsyncBuffer<TSource>( source[Symbol.asyncIterator](), new MaxRefCountList<TSource>() ) : new MemoizeAsyncBuffer<TSource>( source[Symbol.asyncIterator](), new RefCountList<TSource>(readerCount) ); } return create<TSource | TResult>(() => selector!(memoize<TSource>(readerCount)(source))[Symbol.asyncIterator]() ); }; }