async-changeset
Rich Harris 7 months ago
parent 6b058526f3
commit 8c727cced5

@ -57,15 +57,15 @@ export function set_active_boundary(boundary) {
export class Boundary {
suspended = false;
/** @type {Boundary | null} */
parent;
/** @type {TemplateNode} */
#anchor;
/** @type {BoundaryProps} */
#props;
/** @type {Boundary | null} */
#parent;
/** @type {Effect} */
#effect;
@ -102,12 +102,14 @@ export class Boundary {
constructor(node, props, children) {
this.#anchor = node;
this.#props = props;
this.#parent = active_boundary;
this.parent = active_boundary;
active_boundary = this;
this.#effect = block(() => {
var boundary_effect = /** @type {Effect} */ (active_effect);
boundary_effect.b = this;
var hydrate_open = hydrate_node;
const reset = () => {
@ -138,39 +140,6 @@ export class Boundary {
// @ts-ignore We re-use the effect's fn property to avoid allocation of an additional field
boundary_effect.fn = (/** @type {unknown} */ input, /** @type {any} */ payload) => {
if (input === ASYNC_INCREMENT) {
// post-init, show the pending snippet after a timeout
if (!this.suspended && (boundary_effect.f & EFFECT_RAN) !== 0) {
var start = raf.now();
var end = start + (this.#props.showPendingAfter ?? 500);
loop((now) => {
if (this.#pending_count === 0) return false;
if (now < end) return true;
this.#show_pending_snippet(false);
});
}
this.suspended = true;
this.#pending_count++;
return;
}
if (input === ASYNC_DECREMENT) {
if (--this.#pending_count === 0 && !this.#keep_pending_snippet) {
this.commit();
if (this.#main_effect !== null) {
// TODO do we also need to `resume_effect` here?
schedule_effect(this.#main_effect);
}
}
return;
}
var error = input;
var onerror = this.#props.onerror;
let failed = this.#props.failed;
@ -269,6 +238,8 @@ export class Boundary {
reset_is_throwing_error();
}, flags);
this.ran = true;
// @ts-expect-error
this.#effect.fn.boundary = this;
@ -276,7 +247,11 @@ export class Boundary {
this.#anchor = hydrate_node;
}
active_boundary = this.#parent;
active_boundary = this.parent;
}
has_pending_snippet() {
return !!this.#props.pending;
}
/**
@ -336,7 +311,7 @@ export class Boundary {
return true;
});
}
} else if (this.#parent) {
} else if (this.parent) {
throw new Error('TODO show pending snippet on parent');
} else {
throw new Error('no pending snippet to show');
@ -394,10 +369,36 @@ export class Boundary {
}
}
}
}
const ASYNC_INCREMENT = Symbol();
const ASYNC_DECREMENT = Symbol();
increment() {
// post-init, show the pending snippet after a timeout
if (!this.suspended && this.ran) {
var start = raf.now();
var end = start + (this.#props.showPendingAfter ?? 500);
loop((now) => {
if (this.#pending_count === 0) return false;
if (now < end) return true;
this.#show_pending_snippet(false);
});
}
this.suspended = true;
this.#pending_count++;
}
decrement() {
if (--this.#pending_count === 0 && !this.#keep_pending_snippet) {
this.commit();
if (this.#main_effect !== null) {
// TODO do we also need to `resume_effect` here?
schedule_effect(this.#main_effect);
}
}
}
}
var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED | BOUNDARY_EFFECT;
@ -458,19 +459,12 @@ export function capture(track = true) {
};
}
/**
* @param {Effect} boundary
*/
export function is_pending_boundary(boundary) {
// @ts-ignore
return boundary.fn.is_pending();
}
export function suspend() {
var boundary = active_effect;
let boundary = /** @type {Effect} */ (active_effect).b;
while (boundary !== null) {
if ((boundary.f & BOUNDARY_EFFECT) !== 0 && is_pending_boundary(boundary)) {
// TODO pretty sure this is wrong
if (boundary.has_pending_snippet()) {
break;
}
@ -481,12 +475,10 @@ export function suspend() {
e.await_outside_boundary();
}
// @ts-ignore
boundary?.fn(ASYNC_INCREMENT);
boundary.increment();
return function unsuspend() {
// @ts-ignore
boundary?.fn?.(ASYNC_DECREMENT);
boundary.decrement();
};
}

@ -43,7 +43,7 @@ import { DEV } from 'esm-env';
import { define_property } from '../../shared/utils.js';
import { get_next_sibling } from '../dom/operations.js';
import { async_derived, derived } from './deriveds.js';
import { capture, suspend } from '../dom/blocks/boundary.js';
import { active_boundary, capture, suspend } from '../dom/blocks/boundary.js';
import { component_context, dev_current_component_function } from '../context.js';
/**
@ -112,6 +112,7 @@ function create_effect(type, fn, sync, push = true) {
last: null,
next: null,
parent: is_root ? null : parent_effect,
b: parent_effect && parent_effect.b,
prev: null,
teardown: null,
transitions: null,

@ -1,4 +1,5 @@
import type { ComponentContext, Dom, Equals, TemplateNode, TransitionManager } from '#client';
import type { Boundary } from '../dom/blocks/boundary';
export interface Signal {
/** Flags bitmask */
@ -67,6 +68,8 @@ export interface Effect extends Reaction {
last: null | Effect;
/** Parent effect */
parent: Effect | null;
/** THe boundary this effect belongs to */
b: Boundary | null;
/** Dev only */
component_function?: any;
}

Loading…
Cancel
Save