Created
April 19, 2022 17:40
-
-
Save dpmccabe/389c4b059bf7050bac550150ac945c96 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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