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

Lines changed: 8 additions & 1 deletion
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

Lines changed: 100 additions & 107 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy