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

@ -57,15 +57,15 @@ export function set_active_boundary(boundary) {
export class Boundary { export class Boundary {
suspended = false; suspended = false;
/** @type {Boundary | null} */
parent;
/** @type {TemplateNode} */ /** @type {TemplateNode} */
#anchor; #anchor;
/** @type {BoundaryProps} */ /** @type {BoundaryProps} */
#props; #props;
/** @type {Boundary | null} */
#parent;
/** @type {Effect} */ /** @type {Effect} */
#effect; #effect;
@ -102,12 +102,14 @@ export class Boundary {
constructor(node, props, children) { constructor(node, props, children) {
this.#anchor = node; this.#anchor = node;
this.#props = props; this.#props = props;
this.#parent = active_boundary; this.parent = active_boundary;
active_boundary = this; active_boundary = this;
this.#effect = block(() => { this.#effect = block(() => {
var boundary_effect = /** @type {Effect} */ (active_effect); var boundary_effect = /** @type {Effect} */ (active_effect);
boundary_effect.b = this;
var hydrate_open = hydrate_node; var hydrate_open = hydrate_node;
const reset = () => { 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 // @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) => { 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 error = input;
var onerror = this.#props.onerror; var onerror = this.#props.onerror;
let failed = this.#props.failed; let failed = this.#props.failed;
@ -269,6 +238,8 @@ export class Boundary {
reset_is_throwing_error(); reset_is_throwing_error();
}, flags); }, flags);
this.ran = true;
// @ts-expect-error // @ts-expect-error
this.#effect.fn.boundary = this; this.#effect.fn.boundary = this;
@ -276,7 +247,11 @@ export class Boundary {
this.#anchor = hydrate_node; 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; return true;
}); });
} }
} else if (this.#parent) { } else if (this.parent) {
throw new Error('TODO show pending snippet on parent'); throw new Error('TODO show pending snippet on parent');
} else { } else {
throw new Error('no pending snippet to show'); throw new Error('no pending snippet to show');
@ -394,10 +369,36 @@ export class Boundary {
} }
} }
} }
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);
});
} }
const ASYNC_INCREMENT = Symbol(); this.suspended = true;
const ASYNC_DECREMENT = Symbol(); 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; 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() { export function suspend() {
var boundary = active_effect; let boundary = /** @type {Effect} */ (active_effect).b;
while (boundary !== null) { 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; break;
} }
@ -481,12 +475,10 @@ export function suspend() {
e.await_outside_boundary(); e.await_outside_boundary();
} }
// @ts-ignore boundary.increment();
boundary?.fn(ASYNC_INCREMENT);
return function unsuspend() { return function unsuspend() {
// @ts-ignore boundary.decrement();
boundary?.fn?.(ASYNC_DECREMENT);
}; };
} }

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

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

Loading…
Cancel
Save