Skip to content

Commit c3db9ac

Browse files
committed
WIP
1 parent 7c1408d commit c3db9ac

File tree

4 files changed

+87
-69
lines changed

4 files changed

+87
-69
lines changed

packages/svelte/src/internal/client/dom/blocks/boundary.js

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export class Boundary {
8787
/** @type {DocumentFragment | null} */
8888
#offscreen_fragment = null;
8989

90-
#local_pending_count = 0;
90+
local_pending_count = 0;
9191
#pending_count = 0;
9292

9393
#is_creating_fallback = false;
@@ -103,12 +103,12 @@ export class Boundary {
103103

104104
#effect_pending_update = () => {
105105
if (this.#effect_pending) {
106-
internal_set(this.#effect_pending, this.#local_pending_count);
106+
internal_set(this.#effect_pending, this.local_pending_count);
107107
}
108108
};
109109

110110
#effect_pending_subscriber = createSubscriber(() => {
111-
this.#effect_pending = source(this.#local_pending_count);
111+
this.#effect_pending = source(this.local_pending_count);
112112

113113
if (DEV) {
114114
tag(this.#effect_pending, '$effect.pending()');
@@ -285,13 +285,6 @@ export class Boundary {
285285
this.#anchor.before(this.#offscreen_fragment);
286286
this.#offscreen_fragment = null;
287287
}
288-
289-
// TODO this feels like a little bit of a kludge, but until we
290-
// overhaul the boundary/batch relationship it's probably
291-
// the most pragmatic solution available to us
292-
queue_micro_task(() => {
293-
Batch.ensure().flush();
294-
});
295288
}
296289
}
297290

@@ -304,7 +297,7 @@ export class Boundary {
304297
update_pending_count(d) {
305298
this.#update_pending_count(d);
306299

307-
this.#local_pending_count += d;
300+
this.local_pending_count += d;
308301
effect_pending_updates.add(this.#effect_pending_update);
309302
}
310303

@@ -363,7 +356,7 @@ export class Boundary {
363356
// If the failure happened while flushing effects, current_batch can be null
364357
Batch.ensure();
365358

366-
this.#local_pending_count = 0;
359+
this.local_pending_count = 0;
367360

368361
if (this.#failed_effect !== null) {
369362
pause_effect(this.#failed_effect, () => {

packages/svelte/src/internal/client/reactivity/async.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,9 @@ export function unset_context() {
202202
export async function async_body(fn) {
203203
var boundary = get_boundary();
204204
var batch = /** @type {Batch} */ (current_batch);
205-
var pending = boundary.is_pending();
206205

207206
boundary.update_pending_count(1);
208-
if (!pending) batch.increment();
207+
batch.increment();
209208

210209
var active = /** @type {Effect} */ (active_effect);
211210

@@ -238,12 +237,7 @@ export async function async_body(fn) {
238237
}
239238

240239
boundary.update_pending_count(-1);
241-
242-
if (pending) {
243-
batch.flush();
244-
} else {
245-
batch.decrement();
246-
}
240+
batch.decrement();
247241

248242
unset_context();
249243
}

packages/svelte/src/internal/client/reactivity/batch.js

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
RENDER_EFFECT,
1212
ROOT_EFFECT,
1313
MAYBE_DIRTY,
14-
DERIVED
14+
DERIVED,
15+
BOUNDARY_EFFECT
1516
} from '#client/constants';
1617
import { async_mode_flag } from '../../flags/index.js';
1718
import { deferred, define_property } from '../../shared/utils.js';
@@ -30,6 +31,16 @@ import { invoke_error_boundary } from '../error-handling.js';
3031
import { old_values } from './sources.js';
3132
import { unlink_effect } from './effects.js';
3233

34+
/**
35+
* @typedef {{
36+
* parent: EffectTarget | null;
37+
* effect: Effect | null;
38+
* effects: Effect[];
39+
* render_effects: Effect[];
40+
* block_effects: Effect[];
41+
* }} EffectTarget
42+
*/
43+
3344
/** @type {Set<Batch>} */
3445
const batches = new Set();
3546

@@ -97,26 +108,6 @@ export class Batch {
97108
*/
98109
#deferred = null;
99110

100-
/**
101-
* Template effects and `$effect.pre` effects, which run when
102-
* a batch is committed
103-
* @type {Effect[]}
104-
*/
105-
#render_effects = [];
106-
107-
/**
108-
* The same as `#render_effects`, but for `$effect` (which runs after)
109-
* @type {Effect[]}
110-
*/
111-
#effects = [];
112-
113-
/**
114-
* Block effects, which may need to re-run on subsequent flushes
115-
* in order to update internal sources (e.g. each block items)
116-
* @type {Effect[]}
117-
*/
118-
#block_effects = [];
119-
120111
/**
121112
* Deferred effects (which run after async work has completed) that are DIRTY
122113
* @type {Effect[]}
@@ -155,33 +146,26 @@ export class Batch {
155146
if (this.#pending === 0) {
156147
// TODO we need this because we commit _then_ flush effects...
157148
// maybe there's a way we can reverse the order?
158-
var previous_batch_sources = batch_values;
149+
// var previous_batch_sources = batch_values;
159150

160151
this.#commit();
161152

162-
var render_effects = this.#render_effects;
163-
var effects = this.#effects;
164-
165-
this.#render_effects = [];
166-
this.#effects = [];
167-
this.#block_effects = [];
168-
169153
// If sources are written to, then work needs to happen in a separate batch, else prior sources would be mixed with
170154
// newly updated sources, which could lead to infinite loops when effects run over and over again.
171155
previous_batch = this;
172156
current_batch = null;
173157

174-
batch_values = previous_batch_sources;
175-
flush_queued_effects(render_effects);
176-
flush_queued_effects(effects);
158+
// batch_values = previous_batch_sources;
159+
// flush_queued_effects(target.render_effects);
160+
// flush_queued_effects(target.effects);
177161

178162
previous_batch = null;
179163

180164
this.#deferred?.resolve();
181165
} else {
182-
this.#defer_effects(this.#render_effects);
183-
this.#defer_effects(this.#effects);
184-
this.#defer_effects(this.#block_effects);
166+
// this.#defer_effects(target.render_effects);
167+
// this.#defer_effects(target.effects);
168+
// this.#defer_effects(target.block_effects);
185169
}
186170

187171
batch_values = null;
@@ -195,6 +179,17 @@ export class Batch {
195179
#traverse_effect_tree(root) {
196180
root.f ^= CLEAN;
197181

182+
var should_defer = false;
183+
184+
/** @type {EffectTarget} */
185+
var target = {
186+
parent: null,
187+
effect: null,
188+
effects: [],
189+
render_effects: [],
190+
block_effects: []
191+
};
192+
198193
var effect = root.first;
199194

200195
while (effect !== null) {
@@ -204,15 +199,25 @@ export class Batch {
204199

205200
var skip = is_skippable_branch || (flags & INERT) !== 0 || this.skipped_effects.has(effect);
206201

202+
if ((effect.f & BOUNDARY_EFFECT) !== 0 && effect.b?.is_pending()) {
203+
target = {
204+
parent: target,
205+
effect,
206+
effects: [],
207+
render_effects: [],
208+
block_effects: []
209+
};
210+
}
211+
207212
if (!skip && effect.fn !== null) {
208213
if (is_branch) {
209214
effect.f ^= CLEAN;
210215
} else if ((flags & EFFECT) !== 0) {
211-
this.#effects.push(effect);
216+
target.effects.push(effect);
212217
} else if (async_mode_flag && (flags & RENDER_EFFECT) !== 0) {
213-
this.#render_effects.push(effect);
218+
target.render_effects.push(effect);
214219
} else if (is_dirty(effect)) {
215-
if ((effect.f & BLOCK_EFFECT) !== 0) this.#block_effects.push(effect);
220+
if ((effect.f & BLOCK_EFFECT) !== 0) target.block_effects.push(effect);
216221
update_effect(effect);
217222
}
218223

@@ -228,10 +233,41 @@ export class Batch {
228233
effect = effect.next;
229234

230235
while (effect === null && parent !== null) {
236+
if (parent.b !== null) {
237+
var ready = parent.b.local_pending_count === 0;
238+
239+
if (target.parent === null) {
240+
should_defer ||= !ready;
241+
} else if (parent === target.effect) {
242+
if (ready) {
243+
// TODO can this happen?
244+
target.parent.effects.push(...target.effects);
245+
target.parent.render_effects.push(...target.render_effects);
246+
target.parent.block_effects.push(...target.block_effects);
247+
} else {
248+
this.#defer_effects(target.effects);
249+
this.#defer_effects(target.render_effects);
250+
this.#defer_effects(target.block_effects);
251+
}
252+
253+
target = /** @type {EffectTarget} */ (target.parent);
254+
}
255+
}
256+
231257
effect = parent.next;
232258
parent = parent.parent;
233259
}
234260
}
261+
262+
if (should_defer) {
263+
this.#defer_effects(target.effects);
264+
this.#defer_effects(target.render_effects);
265+
this.#defer_effects(target.block_effects);
266+
} else {
267+
// TODO append/detach blocks here as well
268+
flush_queued_effects(target.render_effects);
269+
flush_queued_effects(target.effects);
270+
}
235271
}
236272

237273
/**
@@ -245,8 +281,6 @@ export class Batch {
245281
// mark as clean so they get scheduled if they depend on pending async state
246282
set_signal_status(e, CLEAN);
247283
}
248-
249-
effects.length = 0;
250284
}
251285

252286
/**

packages/svelte/src/internal/client/reactivity/deriveds.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,14 @@ export function async_derived(fn, location) {
136136
if (DEV) current_async_effect = null;
137137

138138
var batch = /** @type {Batch} */ (current_batch);
139-
var pending = boundary.is_pending();
140139

141140
if (should_suspend) {
142141
boundary.update_pending_count(1);
143-
if (!pending) {
144-
batch.increment();
142+
batch.increment();
145143

146-
deferreds.get(batch)?.reject(STALE_REACTION);
147-
deferreds.delete(batch); // delete to ensure correct order in Map iteration below
148-
deferreds.set(batch, d);
149-
}
144+
deferreds.get(batch)?.reject(STALE_REACTION);
145+
deferreds.delete(batch); // delete to ensure correct order in Map iteration below
146+
deferreds.set(batch, d);
150147
}
151148

152149
/**
@@ -156,7 +153,7 @@ export function async_derived(fn, location) {
156153
const handler = (value, error = undefined) => {
157154
current_async_effect = null;
158155

159-
if (!pending) batch.activate();
156+
batch.activate();
160157

161158
if (error) {
162159
if (error !== STALE_REACTION) {
@@ -193,7 +190,7 @@ export function async_derived(fn, location) {
193190

194191
if (should_suspend) {
195192
boundary.update_pending_count(-1);
196-
if (!pending) batch.decrement();
193+
batch.decrement();
197194
}
198195
};
199196

0 commit comments

Comments
 (0)