represent main/pending/failed effects separately, as we do for other blocks

aa
Rich Harris 7 months ago
parent 036001c055
commit cfba900fb1

@ -35,7 +35,8 @@ const ASYNC_DECREMENT = Symbol();
/** /**
* @param {Effect} boundary * @param {Effect} boundary
* @param {() => void} fn * @param {() => Effect | null} fn
* @returns {Effect | null}
*/ */
function with_boundary(boundary, fn) { function with_boundary(boundary, fn) {
var previous_effect = active_effect; var previous_effect = active_effect;
@ -47,7 +48,7 @@ function with_boundary(boundary, fn) {
set_component_context(boundary.ctx); set_component_context(boundary.ctx);
try { try {
fn(); return fn();
} finally { } finally {
set_active_effect(previous_effect); set_active_effect(previous_effect);
set_active_reaction(previous_reaction); set_active_reaction(previous_reaction);
@ -69,11 +70,14 @@ export function boundary(node, props, children) {
var anchor = node; var anchor = node;
block(() => { block(() => {
/** @type {Effect} */ /** @type {Effect | null} */
var boundary_effect; var main_effect = null;
/** @type {Effect | null} */
var pending_effect = null;
/** @type {Effect | null} */ /** @type {Effect | null} */
var offscreen_effect = null; var failed_effect = null;
/** @type {DocumentFragment | null} */ /** @type {DocumentFragment | null} */
var offscreen_fragment = null; var offscreen_fragment = null;
@ -85,32 +89,33 @@ export function boundary(node, props, children) {
/** /**
* @param {() => void} snippet_fn * @param {() => void} snippet_fn
* @returns {Effect | null}
*/ */
function render_snippet(snippet_fn) { function render_snippet(snippet_fn) {
with_boundary(boundary, () => { return with_boundary(boundary, () => {
is_creating_fallback = true; is_creating_fallback = true;
try { try {
boundary_effect = branch(snippet_fn); return branch(snippet_fn);
} catch (error) { } catch (error) {
handle_error(error, boundary, null, boundary.ctx); handle_error(error, boundary, null, boundary.ctx);
return null;
} finally {
reset_is_throwing_error();
is_creating_fallback = false;
} }
reset_is_throwing_error();
is_creating_fallback = false;
}); });
} }
function suspend() { function suspend() {
if (offscreen_effect || !boundary_effect) { if (offscreen_fragment || !main_effect) {
return; return;
} }
var effect = boundary_effect; var effect = main_effect;
offscreen_effect = boundary_effect;
pause_effect( pause_effect(
boundary_effect, effect,
() => { () => {
var node = effect.nodes_start; var node = effect.nodes_start;
var end = effect.nodes_end; var end = effect.nodes_end;
@ -131,34 +136,40 @@ export function boundary(node, props, children) {
const pending = props.pending; const pending = props.pending;
if (pending) { if (pending) {
render_snippet(() => { pending_effect = render_snippet(() => pending(anchor));
pending(anchor);
});
} }
} }
function unsuspend() { function unsuspend() {
if (!offscreen_effect) { if (!offscreen_fragment) {
return; return;
} }
if (boundary_effect) { if (pending_effect !== null) {
destroy_effect(boundary_effect); pause_effect(pending_effect);
} }
boundary_effect = offscreen_effect;
offscreen_effect = null;
anchor.before(/** @type {DocumentFragment} */ (offscreen_fragment)); anchor.before(/** @type {DocumentFragment} */ (offscreen_fragment));
resume_effect(boundary_effect); offscreen_fragment = null;
if (main_effect !== null) {
resume_effect(main_effect);
}
} }
function reset() { function reset() {
pause_effect(boundary_effect); if (failed_effect !== null) {
pause_effect(failed_effect);
}
with_boundary(boundary, () => { main_effect = with_boundary(boundary, () => {
is_creating_fallback = false; is_creating_fallback = false;
boundary_effect = branch(() => children(anchor));
reset_is_throwing_error(); try {
return branch(() => children(anchor));
} finally {
reset_is_throwing_error();
}
}); });
} }
@ -192,9 +203,15 @@ export function boundary(node, props, children) {
onerror?.(error, reset); onerror?.(error, reset);
if (boundary_effect) { if (main_effect) {
destroy_effect(boundary_effect); destroy_effect(main_effect);
} else if (hydrating) { }
if (failed_effect) {
destroy_effect(failed_effect);
}
if (hydrating) {
set_hydrate_node(hydrate_open); set_hydrate_node(hydrate_open);
next(); next();
set_hydrate_node(remove_nodes()); set_hydrate_node(remove_nodes());
@ -202,7 +219,7 @@ export function boundary(node, props, children) {
if (failed) { if (failed) {
queue_boundary_micro_task(() => { queue_boundary_micro_task(() => {
render_snippet(() => { failed_effect = render_snippet(() => {
failed( failed(
anchor, anchor,
() => error, () => error,
@ -223,7 +240,7 @@ export function boundary(node, props, children) {
const pending = props.pending; const pending = props.pending;
if (hydrating && pending) { if (hydrating && pending) {
boundary_effect = branch(() => pending(anchor)); pending_effect = branch(() => pending(anchor));
// ...now what? we need to start rendering `boundary_fn` offscreen, // ...now what? we need to start rendering `boundary_fn` offscreen,
// and either insert the resulting fragment (if nothing suspends) // and either insert the resulting fragment (if nothing suspends)
@ -235,13 +252,14 @@ export function boundary(node, props, children) {
// the pending or main block was rendered for a given // the pending or main block was rendered for a given
// boundary, and hydrate accordingly // boundary, and hydrate accordingly
queueMicrotask(() => { queueMicrotask(() => {
destroy_effect(boundary_effect); destroy_effect(/** @type {Effect} */ (pending_effect));
with_boundary(boundary, () => {
boundary_effect = branch(() => children(anchor)); main_effect = with_boundary(boundary, () => {
return branch(() => children(anchor));
}); });
}); });
} else { } else {
boundary_effect = branch(() => children(anchor)); main_effect = branch(() => children(anchor));
} }
reset_is_throwing_error(); reset_is_throwing_error();

Loading…
Cancel
Save