shelving
Version:
Toolkit for using data in JavaScript.
49 lines (48 loc) • 2.46 kB
JavaScript
import { awaitAbort, awaitRace, getDelay } from "../util/async.js";
import { awaitDispose } from "../util/dispose.js";
import { FetchStore } from "./FetchStore.js";
import { Store } from "./Store.js";
/**
* Store that fetches its values from a remote source by sending a payload to them.
*
* @param payload The initial payload for the store.
* @param value The initial value for the store, or `NONE` if it does not have one yet.
* @param callback An optional callback that, if set, will be called with the current payload when the `fetch()` method is invoked to fetch the next value.
* @param debounce Delay in milliseconds before the fetch is triggered after a payload change. `busy` becomes `true` immediately; the actual fetch waits for the debounce period to expire. If the payload changes again before the delay expires the previous fetch is cancelled and the timer resets.
*/
export class PayloadFetchStore extends FetchStore {
/**
* Store keeping the current payload to send to the fetch on send.
* - New payloads can be set using `this.payload.value`
*/
payload;
// Override to save initial payload and callback.
constructor(payload, value, callback, debounce = 0) {
const payloadStore = new Store(payload);
const fetch = callback &&
(async (signal) => {
if (debounce > 0)
await awaitRace(getDelay(debounce), awaitAbort(signal));
const value = payloadStore.loading ? await awaitRace(payloadStore.next, awaitAbort(signal)) : payloadStore.value;
return callback(value, signal);
});
super(value, fetch);
this.payload = payloadStore;
void _iterate(this);
}
// Implement `AsyncDisposable`
async [Symbol.asyncDispose]() {
await awaitDispose(this.payload, // Send `done: true` to any iterators of the payload store.
super[Symbol.asyncDispose]());
}
}
/**
* Wait for payload changes, and abort any in-flight request, mark stale, then start a fresh fetch.
* - Note that `this.payload` gets disposed in the `PayloadFetchStore` cleanup function which will send `done: true` to this iteration and end it.
*/
async function _iterate(store) {
for await (const _payload of store.payload.next) {
store.invalidate(); // Abort any in-flight fetch and mark stale.
void store.refresh(); // Eagerly start a fresh fetch with the new payload.
}
}