Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/svelte/src/compiler/phases/1-parse/utils/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export function create_fragment(transparent = false) {
metadata: {
transparent,
dynamic: false,
has_await: false,
effect_pending_expressions: []
has_await: false
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,10 @@ export function CallExpression(node, context) {
break;

case '$effect.pending':
if (node.arguments.length > 1) {
e.rune_invalid_arguments_length(node, rune, 'zero or one arguments');
}

if (context.state.expression) {
context.state.expression.has_state = true;
}

if (node.arguments[0]) {
const fragment = /** @type {AST.Fragment} */ (context.state.fragment);

fragment.metadata.effect_pending_expressions.push(
/** @type {Expression} */ (node.arguments[0])
);
}

break;

case '$inspect':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ export interface ClientTransformState extends TransformState {
/** `true` if we're transforming the contents of `<script>` */
readonly is_instance: boolean;

effect_pending: Map<Expression, Identifier>;

readonly transform: Record<
string,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,12 @@ export function CallExpression(node, context) {
);

case '$effect.pending':
if (node.arguments[0]) {
const id = b.id(`$$pending_${context.state.effect_pending.size}`);

context.state.effect_pending.set(
/** @type {Expression} */ (context.visit(node.arguments[0])),
id
);

return b.call('$.get', id);
}

return b.call('$.pending');
return b.call(
'$.pending',
node.arguments.length > 0
? b.thunk(/** @type {Expression} */ (context.visit(node.arguments[0])))
: undefined
);

case '$inspect':
case '$inspect().with':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export function Fragment(node, context) {
memoizer: new Memoizer(),
template: new Template(),
transform: { ...context.state.transform },
effect_pending: new Map(),
metadata: {
namespace,
bound_contenteditable: context.state.metadata.bound_contenteditable
Expand Down Expand Up @@ -153,10 +152,6 @@ export function Fragment(node, context) {

body.push(...state.consts);

for (const [expression, id] of state.effect_pending) {
body.push(b.const(id, b.call('$.pending', b.thunk(expression))));
}

if (has_await) {
body.push(b.if(b.call('$.aborted'), b.return()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,7 @@ export function RegularElement(node, context) {
metadata,
scope: /** @type {Scope} */ (context.state.scopes.get(node.fragment)),
preserve_whitespace:
context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea',
effect_pending: new Map()
context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea'
};

const { hoisted, trimmed } = clean_nodes(
Expand Down Expand Up @@ -379,10 +378,6 @@ export function RegularElement(node, context) {
}
}

for (const [expression, id] of state.effect_pending) {
child_state.init.push(b.const(id, b.call('$.pending', b.thunk(expression))));
}

if (node.fragment.nodes.some((node) => node.type === 'SnippetBlock')) {
// Wrap children in `{...}` to avoid declaration conflicts
context.state.init.push(
Expand Down
1 change: 0 additions & 1 deletion packages/svelte/src/compiler/types/template.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export namespace AST {
*/
dynamic: boolean;
has_await: boolean;
effect_pending_expressions: Expression[];
};
}

Expand Down
50 changes: 20 additions & 30 deletions packages/svelte/src/internal/client/dom/blocks/boundary.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@ import {
import { HYDRATION_START_ELSE } from '../../../../constants.js';
import { component_context, set_component_context } from '../../context.js';
import { handle_error, invoke_error_boundary } from '../../error-handling.js';
import {
block,
branch,
destroy_effect,
inspect_effect,
pause_effect
} from '../../reactivity/effects.js';
import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
import {
active_effect,
active_reaction,
get,
read_pending,
set_active_effect,
set_active_reaction
set_active_reaction,
set_read_pending
} from '../../runtime.js';
import {
hydrate_next,
Expand All @@ -35,11 +31,10 @@ import { queue_micro_task } from '../task.js';
import * as e from '../../errors.js';
import * as w from '../../warnings.js';
import { DEV } from 'esm-env';
import { Batch, current_batch, effect_pending_updates } from '../../reactivity/batch.js';
import { Batch, effect_pending_updates } from '../../reactivity/batch.js';
import { internal_set, source } from '../../reactivity/sources.js';
import { tag } from '../../dev/tracing.js';
import { createSubscriber } from '../../../../reactivity/create-subscriber.js';
import { untrack } from 'svelte';

/**
* @typedef {{
Expand Down Expand Up @@ -455,7 +450,7 @@ export function get_boundary() {
}

/**
* @param {(() => any) | void} fn
* @param {() => any} [fn]
*/
export function pending(fn) {
if (active_effect === null) {
Expand All @@ -465,28 +460,23 @@ export function pending(fn) {
var boundary = active_effect.b;

if (boundary === null) {
return 0; // TODO eventually we will need this to be global
return fn ? fn() : 0; // TODO eventually we will need this to be global
}

if (fn) {
const signal = source(untrack(fn));

inspect_effect(() => {
const value = fn();
let stale = false;

queue_micro_task(() => {
if (stale) return;
internal_set(signal, value);
});
var pending = boundary.get_effect_pending();

return () => {
stale = true;
};
});
if (fn) {
var value;
var prev_read_pending = read_pending;
set_read_pending(true);
try {
value = fn();
} finally {
set_read_pending(prev_read_pending);
}

return signal;
return value;
} else {
return pending;
}

return boundary.get_effect_pending();
}
16 changes: 16 additions & 0 deletions packages/svelte/src/internal/client/reactivity/batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ let last_scheduled_effect = null;
let is_flushing = false;
export let is_flushing_sync = false;

/**
* A map of signal -> pending value
*
* A signal will appear in here if there's pending async work and a signal
* cannot be updated to its new value until that work completes.
*
* Related: `read_pending` in `runtime.js`
*
* @type {Map<Source, { v: any }>}
*/
export let pending_values = new Map();

export class Batch {
/**
* The current values of any sources that are updated in this batch
Expand Down Expand Up @@ -455,6 +467,9 @@ export class Batch {
}
}

var prev_pending = pending_values;
pending_values = current_values;

return () => {
for (const [source, { v, wv }] of current_values) {
// reset the source to the current value (unless
Expand All @@ -465,6 +480,7 @@ export class Batch {
}

batch_deriveds = null;
pending_values = prev_pending;
};
}
}
Expand Down
36 changes: 29 additions & 7 deletions packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ import {
set_dev_stack
} from './context.js';
import * as w from './warnings.js';
import { Batch, batch_deriveds, flushSync, schedule_effect } from './reactivity/batch.js';
import {
Batch,
batch_deriveds,
flushSync,
pending_values,
schedule_effect
} from './reactivity/batch.js';
import { handle_error } from './error-handling.js';
import { UNINITIALIZED } from '../../constants.js';
import { captured_signals } from './legacy.js';
Expand Down Expand Up @@ -144,6 +150,17 @@ export function increment_write_version() {
return ++write_version;
}

/**
* Whether or not we should get the latest value of a signal regardless of whether or not it is pending,
* i.e. inside a boundary with pending async work in which case normally a stale value might be shown.
*/
export let read_pending = false;

/** @param {boolean} value */
export function set_read_pending(value) {
read_pending = value;
}

/**
* Determines whether a derived or effect is dirty.
* If it is MAYBE_DIRTY, will set the status to CLEAN
Expand Down Expand Up @@ -653,20 +670,20 @@ export function get(signal) {
if (is_derived) {
derived = /** @type {Derived} */ (signal);

var value = derived.v;
var derived_value = derived.v;

// if the derived is dirty and has reactions, or depends on the values that just changed, re-execute
// (a derived can be maybe_dirty due to the effect destroy removing its last reaction)
if (
((derived.f & CLEAN) === 0 && derived.reactions !== null) ||
depends_on_old_values(derived)
) {
value = execute_derived(derived);
derived_value = execute_derived(derived);
}

old_values.set(derived, value);
old_values.set(derived, derived_value);

return value;
return derived_value;
}
} else if (is_derived) {
derived = /** @type {Derived} */ (signal);
Expand All @@ -680,11 +697,16 @@ export function get(signal) {
}
}

var value = signal.v;
if (read_pending && pending_values.has(signal)) {
value = /** @type {{v: any}} */ (pending_values.get(signal)).v;
}

if ((signal.f & ERROR_VALUE) !== 0) {
throw signal.v;
throw value;
}

return signal.v;
return value;
}

/** @param {Derived} derived */
Expand Down
Loading