Skip to content

Instantly share code, notes, and snippets.

@dpmccabe
Created April 19, 2022 17:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dpmccabe/389c4b059bf7050bac550150ac945c96 to your computer and use it in GitHub Desktop.
Save dpmccabe/389c4b059bf7050bac550150ac945c96 to your computer and use it in GitHub Desktop.
import { noop, safe_not_equal } from 'svelte/internal';
/** Callback to inform of a value updates. */
export type Subscriber<T> = (value: T) => void;
/** Unsubscribes from value updates. */
export type Unsubscriber = () => void;
/** Callback to update a value. */
export type Updater<T> = (value: T) => T;
/** Cleanup logic callback. */
type Invalidator<T> = (value?: T) => void;
/** Start and stop notification callbacks. */
export type StartStopNotifier<T> = (set: Subscriber<T>) => Unsubscriber | void;
/** Readable interface for subscribing. */
export interface Readable<T> {
/**
* Subscribe on value changes.
* @param run subscription callback
* @param invalidate cleanup callback
*/
subscribe(
this: void,
run: Subscriber<T>,
invalidate?: Invalidator<T>
): Unsubscriber;
get(): T;
}
/** Writable interface for both updating and subscribing. */
export interface Writable<T> extends Readable<T> {
/**
* Set value and inform subscribers.
* @param value to set
*/
set(this: void, value: T): void;
/**
* Update value using callback and inform subscribers.
* @param updater callback
*/
update(this: void, updater: Updater<T>): void;
get(): T;
}
/** Pair of subscriber and invalidator. */
type SubscribeInvalidateTuple<T> = [Subscriber<T>, Invalidator<T>];
const subscriber_queue: any[] = [];
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
export function writable<T>(
value?: T,
start: StartStopNotifier<T> = noop
): Writable<T> {
let stop: Unsubscriber | null;
const subscribers: Set<SubscribeInvalidateTuple<T>> = new Set();
function set(new_value: T): void {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) {
// store is ready
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn: Updater<T>): void {
set(fn(value!));
}
function subscribe(
run: Subscriber<T>,
invalidate: Invalidator<T> = noop
): Unsubscriber {
const subscriber: SubscribeInvalidateTuple<T> = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value!);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0) {
if (stop) {
stop();
}
stop = null;
}
};
}
function get(): T {
return value!;
}
return { set, update, subscribe, get };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment