Skip to content

Commit 826550c

Browse files
fix(reactivity): queuing effects in an array (#13078)
1 parent d523d24 commit 826550c

File tree

2 files changed

+108
-108
lines changed

2 files changed

+108
-108
lines changed

packages/reactivity/src/computed.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ export class ComputedRefImpl<T = any> implements Dependency, Subscriber {
8787
get dep(): Dependency {
8888
return this
8989
}
90-
// for backwards compat
90+
/**
91+
* @internal
92+
* for backwards compat
93+
*/
9194
get _dirty(): boolean {
9295
const flags = this.flags
9396
if (
@@ -99,6 +102,10 @@ export class ComputedRefImpl<T = any> implements Dependency, Subscriber {
99102
}
100103
return false
101104
}
105+
/**
106+
* @internal
107+
* for backwards compat
108+
*/
102109
set _dirty(v: boolean) {
103110
if (v) {
104111
this.flags |= SubscriberFlags.Dirty

packages/reactivity/src/system.ts

+100-107
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable */
2-
// Ported from https://github.com/stackblitz/alien-signals/blob/v1.0.4/src/system.ts
2+
// Ported from https://github.com/stackblitz/alien-signals/blob/v1.0.13/src/system.ts
33
import type { ComputedRefImpl as Computed } from './computed.js'
44
import type { ReactiveEffect as Effect } from './effect.js'
55

@@ -32,9 +32,16 @@ export const enum SubscriberFlags {
3232
Propagated = Dirty | PendingComputed,
3333
}
3434

35+
interface OneWayLink<T> {
36+
target: T
37+
linked: OneWayLink<T> | undefined
38+
}
39+
40+
const notifyBuffer: (Effect | undefined)[] = []
41+
3542
let batchDepth = 0
36-
let queuedEffects: Effect | undefined
37-
let queuedEffectsTail: Effect | undefined
43+
let notifyIndex = 0
44+
let notifyBufferLength = 0
3845

3946
export function startBatch(): void {
4047
++batchDepth
@@ -67,80 +74,81 @@ export function link(dep: Dependency, sub: Subscriber): Link | undefined {
6774
return linkNewDep(dep, sub, nextDep, currentDep)
6875
}
6976

70-
export function propagate(link: Link): void {
77+
export function propagate(current: Link): void {
78+
let next = current.nextSub
79+
let branchs: OneWayLink<Link | undefined> | undefined
80+
let branchDepth = 0
7181
let targetFlag = SubscriberFlags.Dirty
72-
let subs = link
73-
let stack = 0
7482

7583
top: do {
76-
const sub = link.sub
84+
const sub = current.sub
7785
const subFlags = sub.flags
7886

87+
let shouldNotify = false
88+
7989
if (
80-
(!(
90+
!(
8191
subFlags &
8292
(SubscriberFlags.Tracking |
8393
SubscriberFlags.Recursed |
8494
SubscriberFlags.Propagated)
85-
) &&
86-
((sub.flags = subFlags | targetFlag), true)) ||
87-
(subFlags & SubscriberFlags.Recursed &&
88-
!(subFlags & SubscriberFlags.Tracking) &&
89-
((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag),
90-
true)) ||
91-
(!(subFlags & SubscriberFlags.Propagated) &&
92-
isValidLink(link, sub) &&
93-
((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag),
94-
(sub as Dependency).subs !== undefined))
95+
)
96+
) {
97+
sub.flags = subFlags | targetFlag
98+
shouldNotify = true
99+
} else if (
100+
subFlags & SubscriberFlags.Recursed &&
101+
!(subFlags & SubscriberFlags.Tracking)
95102
) {
103+
sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag
104+
shouldNotify = true
105+
} else if (
106+
!(subFlags & SubscriberFlags.Propagated) &&
107+
isValidLink(current, sub)
108+
) {
109+
sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag
110+
shouldNotify = (sub as Dependency).subs !== undefined
111+
}
112+
113+
if (shouldNotify) {
96114
const subSubs = (sub as Dependency).subs
97115
if (subSubs !== undefined) {
116+
current = subSubs
98117
if (subSubs.nextSub !== undefined) {
99-
subSubs.prevSub = subs
100-
link = subs = subSubs
101-
targetFlag = SubscriberFlags.PendingComputed
102-
++stack
103-
} else {
104-
link = subSubs
105-
targetFlag = SubscriberFlags.PendingComputed
118+
branchs = { target: next, linked: branchs }
119+
++branchDepth
120+
next = current.nextSub
106121
}
122+
targetFlag = SubscriberFlags.PendingComputed
107123
continue
108124
}
109125
if (subFlags & SubscriberFlags.Effect) {
110-
if (queuedEffectsTail !== undefined) {
111-
queuedEffectsTail.depsTail!.nextDep = sub.deps
112-
} else {
113-
queuedEffects = sub as Effect
114-
}
115-
queuedEffectsTail = sub as Effect
126+
notifyBuffer[notifyBufferLength++] = sub as Effect
116127
}
117128
} else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) {
118129
sub.flags = subFlags | targetFlag
119130
} else if (
120131
!(subFlags & targetFlag) &&
121132
subFlags & SubscriberFlags.Propagated &&
122-
isValidLink(link, sub)
133+
isValidLink(current, sub)
123134
) {
124135
sub.flags = subFlags | targetFlag
125136
}
126137

127-
if ((link = subs.nextSub!) !== undefined) {
128-
subs = link
129-
targetFlag = stack
138+
if ((current = next!) !== undefined) {
139+
next = current.nextSub
140+
targetFlag = branchDepth
130141
? SubscriberFlags.PendingComputed
131142
: SubscriberFlags.Dirty
132143
continue
133144
}
134145

135-
while (stack) {
136-
--stack
137-
const dep = subs.dep
138-
const depSubs = dep.subs!
139-
subs = depSubs.prevSub!
140-
depSubs.prevSub = undefined
141-
if ((link = subs.nextSub!) !== undefined) {
142-
subs = link
143-
targetFlag = stack
146+
while (branchDepth--) {
147+
current = branchs!.target!
148+
branchs = branchs!.linked
149+
if (current !== undefined) {
150+
next = current.nextSub
151+
targetFlag = branchDepth
144152
? SubscriberFlags.PendingComputed
145153
: SubscriberFlags.Dirty
146154
continue top
@@ -194,35 +202,26 @@ export function processComputedUpdate(
194202
computed: Computed,
195203
flags: SubscriberFlags,
196204
): void {
197-
if (
198-
flags & SubscriberFlags.Dirty ||
199-
(checkDirty(computed.deps!)
200-
? true
201-
: ((computed.flags = flags & ~SubscriberFlags.PendingComputed), false))
202-
) {
205+
if (flags & SubscriberFlags.Dirty || checkDirty(computed.deps!)) {
203206
if (computed.update()) {
204207
const subs = computed.subs
205208
if (subs !== undefined) {
206209
shallowPropagate(subs)
207210
}
208211
}
212+
} else {
213+
computed.flags = flags & ~SubscriberFlags.PendingComputed
209214
}
210215
}
211216

212217
export function processEffectNotifications(): void {
213-
while (queuedEffects !== undefined) {
214-
const effect = queuedEffects
215-
const depsTail = effect.depsTail!
216-
const queuedNext = depsTail.nextDep
217-
if (queuedNext !== undefined) {
218-
depsTail.nextDep = undefined
219-
queuedEffects = queuedNext.sub as Effect
220-
} else {
221-
queuedEffects = undefined
222-
queuedEffectsTail = undefined
223-
}
218+
while (notifyIndex < notifyBufferLength) {
219+
const effect = notifyBuffer[notifyIndex]!
220+
notifyBuffer[notifyIndex++] = undefined
224221
effect.notify()
225222
}
223+
notifyIndex = 0
224+
notifyBufferLength = 0
226225
}
227226

228227
function linkNewDep(
@@ -259,15 +258,18 @@ function linkNewDep(
259258
return newLink
260259
}
261260

262-
function checkDirty(link: Link): boolean {
263-
let stack = 0
261+
function checkDirty(current: Link): boolean {
262+
let prevLinks: OneWayLink<Link> | undefined
263+
let checkDepth = 0
264264
let dirty: boolean
265265

266266
top: do {
267267
dirty = false
268-
const dep = link.dep
268+
const dep = current.dep
269269

270-
if ('flags' in dep) {
270+
if (current.sub.flags & SubscriberFlags.Dirty) {
271+
dirty = true
272+
} else if ('flags' in dep) {
271273
const depFlags = dep.flags
272274
if (
273275
(depFlags & (SubscriberFlags.Computed | SubscriberFlags.Dirty)) ===
@@ -285,58 +287,49 @@ function checkDirty(link: Link): boolean {
285287
(SubscriberFlags.Computed | SubscriberFlags.PendingComputed)) ===
286288
(SubscriberFlags.Computed | SubscriberFlags.PendingComputed)
287289
) {
288-
const depSubs = dep.subs!
289-
if (depSubs.nextSub !== undefined) {
290-
depSubs.prevSub = link
290+
if (current.nextSub !== undefined || current.prevSub !== undefined) {
291+
prevLinks = { target: current, linked: prevLinks }
291292
}
292-
link = dep.deps!
293-
++stack
293+
current = dep.deps!
294+
++checkDepth
294295
continue
295296
}
296297
}
297298

298-
if (!dirty && link.nextDep !== undefined) {
299-
link = link.nextDep
299+
if (!dirty && current.nextDep !== undefined) {
300+
current = current.nextDep
300301
continue
301302
}
302303

303-
if (stack) {
304-
let sub = link.sub as Computed
305-
do {
306-
--stack
307-
const subSubs = sub.subs!
308-
309-
if (dirty) {
310-
if (sub.update()) {
311-
if ((link = subSubs.prevSub!) !== undefined) {
312-
subSubs.prevSub = undefined
313-
shallowPropagate(subSubs)
314-
sub = link.sub as Computed
315-
} else {
316-
sub = subSubs.sub as Computed
317-
}
318-
continue
319-
}
320-
} else {
321-
sub.flags &= ~SubscriberFlags.PendingComputed
322-
}
323-
324-
if ((link = subSubs.prevSub!) !== undefined) {
325-
subSubs.prevSub = undefined
326-
if (link.nextDep !== undefined) {
327-
link = link.nextDep
328-
continue top
329-
}
330-
sub = link.sub as Computed
331-
} else {
332-
if ((link = subSubs.nextDep!) !== undefined) {
333-
continue top
304+
while (checkDepth) {
305+
--checkDepth
306+
const sub = current.sub as Computed
307+
const firstSub = sub.subs!
308+
if (dirty) {
309+
if (sub.update()) {
310+
if (firstSub.nextSub !== undefined) {
311+
current = prevLinks!.target
312+
prevLinks = prevLinks!.linked
313+
shallowPropagate(firstSub)
314+
} else {
315+
current = firstSub
334316
}
335-
sub = subSubs.sub as Computed
317+
continue
336318
}
337-
338-
dirty = false
339-
} while (stack)
319+
} else {
320+
sub.flags &= ~SubscriberFlags.PendingComputed
321+
}
322+
if (firstSub.nextSub !== undefined) {
323+
current = prevLinks!.target
324+
prevLinks = prevLinks!.linked
325+
} else {
326+
current = firstSub
327+
}
328+
if (current.nextDep !== undefined) {
329+
current = current.nextDep
330+
continue top
331+
}
332+
dirty = false
340333
}
341334

342335
return dirty

0 commit comments

Comments
 (0)