@johngw/stream
Version:
Reactive programming tools using the WHATWG Streams API.
114 lines • 2.65 kB
JavaScript
import { timeout } from '@johngw/stream-common/Async';
/**
* The source of a `ReadableStream` that queues items
* only when a timed cache becomes invalid.
*
* @group Sources
* @see {@link StorageCache}
* @see {@link CachableStream:class}
* @example
* Caching for 30 minutes.
*
* ```
* const cache = new StorageCache(
* localStorage,
* 'my-cache',
* 30 * 60_000
* )
*
* let i = 0
* const stream = new ReadableStream(
* new CachableSource<number>(
* cache,
* ['numbers'],
* () => ++i
* )
* )
*
* stream.pipeTo(write(console.info))
* // 1
*
* await setTimeout(30 * 60_000))
* // 2
* ```
* @example
* Manually clearing cache.
*
* ```
* const cache = new StorageCache(
* localStorage,
* 'my-cache',
* 30 * 60_000
* )
*
* let i = 0
* const source = new CachableSource<number>(
* cache,
* ['numbers'],
* () => ++i
* )
*
* const stream = new ReadableStream(source)
*
* stream.pipeTo(write(console.info))
* // 1
*
* source.clear()
* // 2
* ```
*/
export class CachableSource {
#abortController = new AbortController();
#sourceHasFinished = false;
#cache;
#ms;
#path;
#pullResult;
constructor(cache, path, pullResult, ms = cache.ms) {
this.#cache = cache;
this.#ms = ms;
this.#path = path;
this.#pullResult = pullResult;
}
async pull(controller) {
if (this.#sourceHasFinished) {
const item = this.#cache.get(this.#path);
return item === undefined
? controller.close()
: controller.enqueue(item);
}
else if (await this.#cache.updateIfStale(this.#path, this.#pullItem, this.#ms))
controller.enqueue(this.#cache.get(this.#path));
else
await this.#wait(this.#cache.timeLeft(this.#path));
if (controller.desiredSize)
return this.pull(controller);
}
cancel(reason) {
this.#abortController.abort(reason);
}
#pullItem = async () => {
const result = await this.#pullResult();
if (result.done)
this.#sourceHasFinished = true;
else
return result.value;
};
async #wait(ms) {
try {
await timeout(ms, undefined, this.#abortController.signal);
}
catch (error) {
//
}
}
clear() {
this.#cache.unset(this.#path);
this.#abortController.abort('clear');
this.#abortController = new AbortController();
}
get sourceHasFinished() {
return this.#sourceHasFinished;
}
}
//# sourceMappingURL=CachableSource.js.map