Skip to content

Commit 8a1ab92

Browse files
committed
Merge branch 'async-local-effect-pending' into async
2 parents 8300327 + c04a13b commit 8a1ab92

File tree

8 files changed

+62
-44
lines changed

8 files changed

+62
-44
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function CallExpression(node, context) {
6464
);
6565

6666
case '$effect.pending':
67-
return b.call('$.get', b.id('$.pending'));
67+
return b.call(b.id('$.pending'));
6868

6969
case '$inspect':
7070
case '$inspect().with':

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export async function async(node, expressions, fn) {
1717
var restore = capture();
1818
var boundary = get_pending_boundary();
1919

20-
boundary.increment();
20+
boundary.update_pending_count(1);
2121

2222
try {
2323
const result = await Promise.all(expressions.map((fn) => async_derived(fn)));
@@ -29,6 +29,6 @@ export async function async(node, expressions, fn) {
2929
} catch (error) {
3030
boundary.error(error);
3131
} finally {
32-
boundary.decrement();
32+
boundary.update_pending_count(-1);
3333
}
3434
}

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

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { block, branch, destroy_effect, pause_effect } from '../../reactivity/ef
77
import {
88
active_effect,
99
active_reaction,
10+
get,
1011
set_active_effect,
1112
set_active_reaction
1213
} from '../../runtime.js';
@@ -24,6 +25,8 @@ import * as e from '../../../shared/errors.js';
2425
import { DEV } from 'esm-env';
2526
import { from_async_derived, set_from_async_derived } from '../../reactivity/deriveds.js';
2627
import { Batch } from '../../reactivity/batch.js';
28+
import { source, update } from '../../reactivity/sources.js';
29+
import { tag } from '../../dev/tracing.js';
2730

2831
/**
2932
* @typedef {{
@@ -82,6 +85,8 @@ export class Boundary {
8285
#pending_count = 0;
8386
#is_creating_fallback = false;
8487

88+
effect_pending = source(0);
89+
8590
/**
8691
* @param {TemplateNode} node
8792
* @param {BoundaryProps} props
@@ -98,6 +103,10 @@ export class Boundary {
98103

99104
this.pending = !!this.#props.pending;
100105

106+
if (DEV) {
107+
tag(this.effect_pending, '$effect.pending()');
108+
}
109+
101110
this.#effect = block(() => {
102111
/** @type {Effect} */ (active_effect).b = this;
103112

@@ -210,19 +219,26 @@ export class Boundary {
210219
}
211220
}
212221

213-
increment() {
214-
this.#pending_count++;
215-
}
222+
/** @param {1 | -1} d */
223+
#update_pending_count(d) {
224+
this.#pending_count += d;
216225

217-
decrement() {
218-
if (--this.#pending_count === 0) {
226+
if (this.#pending_count === 0) {
219227
this.commit();
228+
}
229+
}
220230

221-
if (this.#main_effect !== null) {
222-
// TODO do we also need to `resume_effect` here?
223-
// schedule_effect(this.#main_effect);
224-
}
231+
/** @param {1 | -1} d */
232+
update_pending_count(d) {
233+
if (this.has_pending_snippet()) {
234+
this.#update_pending_count(d);
235+
} else if (this.parent) {
236+
this.parent.#update_pending_count(d);
225237
}
238+
239+
queueMicrotask(() => {
240+
update(this.effect_pending, d);
241+
});
226242
}
227243

228244
/** @param {unknown} error */
@@ -373,10 +389,10 @@ export function capture(track = true) {
373389
export function suspend() {
374390
let boundary = get_pending_boundary();
375391

376-
boundary.increment();
392+
boundary.update_pending_count(1);
377393

378394
return function unsuspend() {
379-
boundary.decrement();
395+
boundary.update_pending_count(-1);
380396
};
381397
}
382398

@@ -401,3 +417,14 @@ function exit() {
401417
set_active_reaction(null);
402418
set_component_context(null);
403419
}
420+
421+
export function pending() {
422+
// TODO throw helpful error if called outside an effect
423+
const boundary = /** @type {Effect} */ (active_effect).b;
424+
425+
if (boundary === null) {
426+
return 0; // TODO eventually we will need this to be global
427+
}
428+
429+
return get(boundary.effect_pending);
430+
}

packages/svelte/src/internal/client/index.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,7 @@ export {
115115
user_effect,
116116
user_pre_effect
117117
} from './reactivity/effects.js';
118-
export {
119-
mutable_source,
120-
mutate,
121-
pending,
122-
set,
123-
state,
124-
update,
125-
update_pre
126-
} from './reactivity/sources.js';
118+
export { mutable_source, mutate, set, state, update, update_pre } from './reactivity/sources.js';
127119
export {
128120
prop,
129121
rest_props,
@@ -143,7 +135,7 @@ export {
143135
update_store,
144136
mark_store_binding
145137
} from './reactivity/store.js';
146-
export { boundary, save, suspend } from './dom/blocks/boundary.js';
138+
export { boundary, pending, save, suspend } from './dom/blocks/boundary.js';
147139
export { set_text } from './render.js';
148140
export {
149141
get,

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

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,13 @@ import {
1010
set_signal_status,
1111
update_effect
1212
} from '../runtime.js';
13-
import { raf } from '../timing.js';
14-
import { internal_set, pending } from './sources.js';
1513

1614
/** @type {Set<Batch>} */
1715
const batches = new Set();
1816

1917
/** @type {Batch | null} */
2018
export let current_batch = null;
2119

22-
/** Update `$effect.pending()` */
23-
function update_pending() {
24-
internal_set(pending, batches.size > 0);
25-
}
26-
2720
/** @type {Map<Derived, any> | null} */
2821
export let batch_deriveds = null;
2922

@@ -239,8 +232,6 @@ export class Batch {
239232
}
240233

241234
this.#callbacks.clear();
242-
243-
raf.tick(update_pending);
244235
}
245236

246237
increment() {
@@ -295,10 +286,6 @@ export class Batch {
295286

296287
static ensure() {
297288
if (current_batch === null) {
298-
if (batches.size === 0) {
299-
raf.tick(update_pending);
300-
}
301-
302289
const batch = (current_batch = new Batch());
303290
batches.add(current_batch);
304291

@@ -315,3 +302,13 @@ export class Batch {
315302
return current_batch;
316303
}
317304
}
305+
306+
/**
307+
* Forcibly remove all current batches
308+
* TODO investigate why we need this in tests
309+
*/
310+
export function clear() {
311+
for (const batch of batches) {
312+
batch.remove();
313+
}
314+
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { destroy_effect, render_effect } from './effects.js';
3131
import { inspect_effects, internal_set, set_inspect_effects, source } from './sources.js';
3232
import { get_stack } from '../dev/tracing.js';
3333
import { tracing_mode_flag } from '../../flags/index.js';
34-
import { get_pending_boundary } from '../dom/blocks/boundary.js';
34+
import { Boundary, get_pending_boundary } from '../dom/blocks/boundary.js';
3535
import { component_context } from '../context.js';
3636
import { UNINITIALIZED } from '../../../constants.js';
3737
import { current_batch } from './batch.js';
@@ -105,7 +105,7 @@ export function async_derived(fn, location) {
105105
throw new Error('TODO cannot create unowned async derived');
106106
}
107107

108-
let boundary = get_pending_boundary();
108+
var boundary = /** @type {Boundary} */ (parent.b);
109109

110110
var promise = /** @type {Promise<V>} */ (/** @type {unknown} */ (undefined));
111111
var signal = source(/** @type {V} */ (UNINITIALIZED));
@@ -135,7 +135,8 @@ export function async_derived(fn, location) {
135135
var pending = boundary.pending;
136136

137137
if (should_suspend) {
138-
(pending ? boundary : batch).increment();
138+
boundary.update_pending_count(1);
139+
if (!pending) batch.increment();
139140
}
140141

141142
/**
@@ -148,7 +149,8 @@ export function async_derived(fn, location) {
148149
from_async_derived = null;
149150

150151
if (should_suspend) {
151-
(pending ? boundary : batch).decrement();
152+
boundary.update_pending_count(-1);
153+
if (!pending) batch.decrement();
152154
}
153155

154156
if (!pending) batch.restore();

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ export let inspect_effects = new Set();
4343
/** @type {Map<Source, any>} */
4444
export const old_values = new Map();
4545

46-
/** Internal representation of `$effect.pending()` */
47-
export let pending = source(false);
48-
4946
/**
5047
* @param {Set<any>} v
5148
*/

packages/svelte/tests/runtime-legacy/shared.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { assert_html_equal, assert_html_equal_with_options } from '../html_equal
1111
import { raf } from '../animation-helpers.js';
1212
import type { CompileOptions } from '#compiler';
1313
import { suite_with_variants, type BaseTest } from '../suite.js';
14+
import { clear } from '../../src/internal/client/reactivity/batch.js';
1415

1516
type Assert = typeof import('vitest').assert & {
1617
htmlEqual(a: string, b: string, description?: string): void;
@@ -521,6 +522,8 @@ async function run_test_variant(
521522
console.log = console_log;
522523
console.warn = console_warn;
523524
console.error = console_error;
525+
526+
clear();
524527
}
525528
}
526529

0 commit comments

Comments
 (0)