Skip to content

Commit 59f4f46

Browse files
committed
add single recomputeDependents to batch
1 parent 023480c commit 59f4f46

File tree

2 files changed

+47
-35
lines changed

2 files changed

+47
-35
lines changed

src/vanilla/store.ts

+34-33
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,12 @@ const addDependency = <Value>(
172172
type BatchPriority = 0 | 1 | 2
173173

174174
type Batch = [
175-
/** high priority listeners */
176-
priority0: Set<() => void>,
175+
/** finish recompute */
176+
priority0: Set<(batch: Batch) => void>,
177177
/** atom listeners */
178-
priority1: Set<() => void>,
178+
priority1: Set<(batch: Batch) => void>,
179179
/** atom mount hooks */
180-
priority2: Set<() => void>,
180+
priority2: Set<(batch: Batch) => void>,
181181
] & {
182182
/** changed Atoms */
183183
C: Set<AnyAtom>
@@ -189,7 +189,7 @@ const createBatch = (): Batch =>
189189
const addBatchFunc = (
190190
batch: Batch,
191191
priority: BatchPriority,
192-
fn: () => void,
192+
fn: (batch: Batch) => void,
193193
) => {
194194
batch[priority].add(fn)
195195
}
@@ -203,21 +203,20 @@ const registerBatchAtom = (
203203
batch.C.add(atom)
204204
atomState.u?.(batch)
205205
const scheduleListeners = () => {
206-
atomState.m?.l.forEach((listener) => addBatchFunc(batch, 1, listener))
206+
atomState.m?.l.forEach((listener) =>
207+
addBatchFunc(batch, 1, () => listener()),
208+
)
207209
}
208210
addBatchFunc(batch, 1, scheduleListeners)
209211
}
210212
}
211213

212-
const flushBatch = (
213-
batch: Batch,
214-
recomputeDependents: (batch: Batch) => void,
215-
) => {
214+
const flushBatch = (batch: Batch) => {
216215
let error: AnyError
217216
let hasError = false
218-
const call = (fn: () => void) => {
217+
const call = (fn: (batch: Batch) => void) => {
219218
try {
220-
fn()
219+
fn(batch)
221220
} catch (e) {
222221
if (!hasError) {
223222
error = e
@@ -226,8 +225,6 @@ const flushBatch = (
226225
}
227226
}
228227
while (batch.C.size || batch.some((channel) => channel.size)) {
229-
recomputeDependents(batch)
230-
batch.C.clear()
231228
for (const channel of batch) {
232229
channel.forEach(call)
233230
channel.clear()
@@ -380,7 +377,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
380377
const batch = createBatch()
381378
addDependency(atom, atomState, a, aState)
382379
mountDependencies(batch, atom, atomState)
383-
recomputeAndFlushBatch(batch)
380+
flushBatch(batch)
384381
}
385382
}
386383
}
@@ -422,7 +419,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
422419
if (atomState.m) {
423420
const batch = createBatch()
424421
mountDependencies(batch, atom, atomState)
425-
recomputeAndFlushBatch(batch)
422+
flushBatch(batch)
426423
}
427424
}
428425
valueOrPromise.then(complete, complete)
@@ -441,18 +438,23 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
441438
const readAtom = <Value>(atom: Atom<Value>): Value =>
442439
returnAtomValue(readAtomState(undefined, atom))
443440

444-
const getMountedDependents = <Value>(
441+
const getMountedOrBatchDependents = <Value>(
445442
atomState: AtomState<Value>,
446443
): Map<AnyAtom, AtomState> => {
447-
const mountedDependents = new Map<AnyAtom, AtomState>()
448-
const dependents = new Set([...(atomState.m?.t || []), ...atomState.p])
449-
for (const a of dependents) {
444+
const dependents = new Map<AnyAtom, AtomState>()
445+
for (const a of atomState.m?.t || []) {
450446
const aState = ensureAtomState(a)
451447
if (aState.m) {
452-
mountedDependents.set(a, aState)
448+
dependents.set(a, aState)
453449
}
454450
}
455-
return mountedDependents
451+
for (const atomWithPendingPromise of atomState.p) {
452+
dependents.set(
453+
atomWithPendingPromise,
454+
ensureAtomState(atomWithPendingPromise),
455+
)
456+
}
457+
return dependents
456458
}
457459

458460
const dirtyDependents = <Value>(atomState: AtomState<Value>) => {
@@ -465,7 +467,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
465467
continue
466468
}
467469
aState.x = true
468-
for (const [, s] of getMountedDependents(aState)) {
470+
for (const [, s] of getMountedOrBatchDependents(aState)) {
469471
if (!dependents.has(s)) {
470472
dependents.add(s)
471473
stack.push(s)
@@ -512,7 +514,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
512514
}
513515
visiting.add(a)
514516
// Push unvisited dependents onto the stack
515-
for (const [d, s] of getMountedDependents(aState)) {
517+
for (const [d, s] of getMountedOrBatchDependents(aState)) {
516518
if (a !== d && !visiting.has(d)) {
517519
stack.push([d, s])
518520
}
@@ -539,11 +541,9 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
539541
}
540542
delete aState.x
541543
}
544+
batch.C.clear()
542545
}
543546

544-
const recomputeAndFlushBatch = (batch: Batch) =>
545-
flushBatch(batch, recomputeDependents)
546-
547547
const writeAtomState = <Value, Args extends unknown[], Result>(
548548
batch: Batch,
549549
atom: WritableAtom<Value, Args, Result>,
@@ -570,14 +570,15 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
570570
if (prevEpochNumber !== aState.n) {
571571
dirtyDependents(aState)
572572
registerBatchAtom(batch, a, aState)
573+
addBatchFunc(batch, 0, recomputeDependents)
573574
}
574575
return undefined as R
575576
} else {
576577
return writeAtomState(batch, a, ...args)
577578
}
578579
} finally {
579580
if (!isSync) {
580-
recomputeAndFlushBatch(batch)
581+
flushBatch(batch)
581582
}
582583
}
583584
}
@@ -596,7 +597,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
596597
try {
597598
return writeAtomState(batch, atom, ...args)
598599
} finally {
599-
recomputeAndFlushBatch(batch)
600+
flushBatch(batch)
600601
}
601602
}
602603

@@ -653,7 +654,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
653654
return writeAtomState(batch, atom, ...args)
654655
} finally {
655656
if (!isSync) {
656-
recomputeAndFlushBatch(batch)
657+
flushBatch(batch)
657658
}
658659
}
659660
}
@@ -690,7 +691,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
690691
// unmount self
691692
const onUnmount = atomState.m.u
692693
if (onUnmount) {
693-
addBatchFunc(batch, 2, () => onUnmount(batch))
694+
addBatchFunc(batch, 2, onUnmount)
694695
}
695696
delete atomState.m
696697
atomState.h?.(batch)
@@ -710,12 +711,12 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
710711
const mounted = mountAtom(batch, atom, atomState)
711712
const listeners = mounted.l
712713
listeners.add(listener)
713-
recomputeAndFlushBatch(batch)
714+
flushBatch(batch)
714715
return () => {
715716
listeners.delete(listener)
716717
const batch = createBatch()
717718
unmountAtom(batch, atom, atomState)
718-
recomputeAndFlushBatch(batch)
719+
flushBatch(batch)
719720
}
720721
}
721722

tests/vanilla/effect.test.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ type Store = ReturnType<typeof createStore>
66
type GetAtomState = Parameters<Parameters<Store['unstable_derive']>[0]>[0]
77
type AtomState = NonNullable<ReturnType<GetAtomState>>
88
type AnyAtom = Atom<unknown>
9+
type Batch = Parameters<NonNullable<AtomState['u']>>[0]
910

1011
type Cleanup = () => void
1112
type Effect = (get: Getter, set: Setter) => Cleanup | void
@@ -59,7 +60,7 @@ function syncEffect(effect: Effect): Atom<void> {
5960
store.set(refreshAtom, (v) => v + 1)
6061
} else {
6162
// unmount
62-
batch[0].add(() => {
63+
scheduleListener(batch, () => {
6364
ref.cleanup?.()
6465
delete ref.cleanup
6566
})
@@ -69,7 +70,17 @@ function syncEffect(effect: Effect): Atom<void> {
6970
internalAtomState.u = (batch) => {
7071
originalUpdateHook?.(batch)
7172
// update
72-
batch[0].add(runEffect)
73+
scheduleListener(batch, runEffect)
74+
}
75+
function scheduleListener(batch: Batch, listener: () => void) {
76+
if (batch[0].size === 0) {
77+
store.set(
78+
atom(0, function (this: any, _, set) {
79+
set(this, 1)
80+
}),
81+
)
82+
}
83+
batch[0].add(listener)
7384
}
7485
}
7586
return atom((get) => {

0 commit comments

Comments
 (0)