-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsignal.ts
87 lines (68 loc) · 2.62 KB
/
signal.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import {queueMicrotask} from './queueMicrotask'
type Reaction = () => void
const dependenciesOfReactions = new Map<Reaction, Set<usize>>() // TODO WeakMap
const dependentsOfSignals = new Map<usize, Set<Reaction>>() // TODO WeakMap
let currentReaction: Reaction | null = null
let scheduledReactions = new Set<Reaction>()
// There is no destructuring (yet), so we use a class with getter and setter for now.
export class Signal<T> {
constructor(private value: T) {}
get(/*TODO previous: T*/): T {
if (currentReaction) trackDependency(changetype<usize>(this), currentReaction!)
return this.value
}
set(newValue: T): T {
if (dependentsOfSignals.has(changetype<usize>(this)))
scheduleReactions(dependentsOfSignals.get(changetype<usize>(this))!)
return (this.value = newValue)
}
}
export function createSignal<T>(initialValue: T): Signal<T> {
return new Signal(initialValue)
}
export function createEffect<T extends Reaction>(effect: T): void {
runReaction(effect) // Run the reaction immediately initially.
}
function trackDependency(signal: usize, reaction: Reaction): void {
if (!dependenciesOfReactions.has(reaction)) dependenciesOfReactions.set(reaction, new Set())
dependenciesOfReactions.get(reaction)!.add(signal)
if (!dependentsOfSignals.has(signal)) dependentsOfSignals.set(signal, new Set())
dependentsOfSignals.get(signal)!.add(reaction)
}
let areScheduled = false
function scheduleReactions(reactions: Set<Reaction>): void {
const _reactions = Set_values(reactions)
for (let i = 0, l = _reactions.length; i < l; i += 1) scheduleReaction(_reactions[i])
}
// Super simple microtask scheduling, does not have dynamic re-ordering while executing yet
function scheduleReaction(reaction: Reaction): void {
scheduledReactions.add(reaction)
if (areScheduled) return
areScheduled = true
queueMicrotask(() => {
areScheduled = false
runReactions()
})
}
function runReactions(): void {
const reactions = Set_values(scheduledReactions)
scheduledReactions.clear()
for (let i = 0, l = reactions.length; i < l; i += 1) runReaction(reactions[i])
}
function runReaction(reaction: Reaction): void {
// Clear dependencies before running the reaction to recalculate them so
// that signals in unused branches don't trigger re-runs.
if (dependenciesOfReactions.has(reaction)) {
const dependencies = dependenciesOfReactions.get(reaction)
const _deps = Set_values(dependencies!)
dependencies!.clear()
for (let i = 0, l = _deps.length; i < l; i += 1) {
const signal = _deps[i]
const dependents = dependentsOfSignals.get(signal)
dependents!.delete(reaction)
}
}
currentReaction = reaction
reaction()
currentReaction = null
}