Skip to content

Instantly share code, notes, and snippets.

@brecert
Last active November 30, 2022 22:29
Show Gist options
  • Save brecert/14fd9e33e0f2d71b11b229be9fcabca0 to your computer and use it in GitHub Desktop.
Save brecert/14fd9e33e0f2d71b11b229be9fcabca0 to your computer and use it in GitHub Desktop.
// Utils
type Fn<T = void> = () => T
type None = typeof None
const None = Symbol()
const $batched: Set<Fn> = new Set()
// The behavior for batches will be different from wires
// unsure why but it feels right
let $inBatch = 0
export function batch(fn: Fn) {
$inBatch++
fn()
if ($inBatch <= 1) {
const fns = [...$batched.values()]
$batched.clear()
fns.map(fn => fn())
}
$inBatch--
}
const callBatched = (fn: Fn) => $inBatch > 0
? $batched.add(fn)
: fn()
// Signals
const Signal = Symbol()
export type Signal<T> = {
readonly [Signal]: symbol
get value(): T
set value(value: T)
}
const $signals: Record<symbol, Set<Fn>> = {}
export function signal<T>(val: T) {
const id = Symbol();
const on = ($signals[id] = new Set)
return {
[Signal]: id,
get value() {
return val
},
set value(value: T) {
val = value
on.forEach(callBatched)
},
dispose() {
$signals[id].clear()
}
}
}
export function compute<R, T>(fn: Fn<R>, signals: Signal<T>[]) {
let result = signal<R>(None as R);
let run = () => batch(() => (result.value = fn()))
let update = on(signals, run)
return {
[Signal]: result[Signal],
get value() {
return result.value === None
? (result.value = fn())
: result.value
},
dispose() {
update.unsubscribe()
result.dispose()
}
}
}
export function on<R, T>(signals: Signal<T>[], fn: Fn<R>) {
signals.forEach(signal => $signals[signal[Signal]].add(fn))
return {
unsubscribe() {
signals.forEach(signal => $signals[signal[Signal]].delete(fn))
}
}
}
const a = signal(1)
const b = signal(1)
const dbl = compute(() => a.value + b.value, [ a, b ])
on([ dbl ], () => {
console.log('on', dbl.value, a.value, b.value)
})
batch(() => {
a.value = 2
b.value = 4
})
// Utils
type Fn<T = void> = () => T
type None = typeof None
const None = Symbol()
const $batched: Set<Fn> = new Set()
// The behavior for batches will be different from wires
// unsure why but it feels right
let $inBatch = 0
export function batch(fn: Fn) {
$inBatch++
fn()
if ($inBatch <= 1) {
const fns = [...$batched.values()]
$batched.clear()
fns.map(fn => fn())
}
$inBatch--
}
const callBatched = (fn: Fn) => $inBatch > 0
? $batched.add(fn)
: fn()
// Signals
const Signal = Symbol()
export type Signal<T> = {
readonly [Signal]: symbol
get value(): T
set value(value: T)
}
const $signals: Record<symbol, Set<Fn>> = {}
export function signal<T>(val: T) {
const id = Symbol();
const on = ($signals[id] = new Set)
return {
[Signal]: id,
get value() {
return val
},
set value(value: T) {
val = value
on.forEach(callBatched)
},
dispose() {
$signals[id].clear()
}
}
}
export function compute<R, T>(fn: Fn<R>, signals: Signal<T>[]) {
let result = signal<R>(None as R);
let run = () => batch(() => (result.value = fn()))
let update = on(signals, run)
return {
[Signal]: result[Signal],
get value() {
return result.value === None
? (result.value = fn())
: result.value
},
dispose() {
update.unsubscribe()
result.dispose()
}
}
}
export function on<R, T>(signals: Signal<T>[], fn: Fn<R>) {
signals.forEach(signal => $signals[signal[Signal]].add(fn))
return {
unsubscribe() {
signals.forEach(signal => $signals[signal[Signal]].delete(fn))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment