Merge branch 'async-local-effect-pending' into async

pull/15844/head
Rich Harris 3 months ago
commit 8a1ab922ce

@ -64,7 +64,7 @@ export function CallExpression(node, context) {
);
case '$effect.pending':
return b.call('$.get', b.id('$.pending'));
return b.call(b.id('$.pending'));
case '$inspect':
case '$inspect().with':

@ -17,7 +17,7 @@ export async function async(node, expressions, fn) {
var restore = capture();
var boundary = get_pending_boundary();
boundary.increment();
boundary.update_pending_count(1);
try {
const result = await Promise.all(expressions.map((fn) => async_derived(fn)));
@ -29,6 +29,6 @@ export async function async(node, expressions, fn) {
} catch (error) {
boundary.error(error);
} finally {
boundary.decrement();
boundary.update_pending_count(-1);
}
}

@ -7,6 +7,7 @@ import { block, branch, destroy_effect, pause_effect } from '../../reactivity/ef
import {
active_effect,
active_reaction,
get,
set_active_effect,
set_active_reaction
} from '../../runtime.js';
@ -24,6 +25,8 @@ import * as e from '../../../shared/errors.js';
import { DEV } from 'esm-env';
import { from_async_derived, set_from_async_derived } from '../../reactivity/deriveds.js';
import { Batch } from '../../reactivity/batch.js';
import { source, update } from '../../reactivity/sources.js';
import { tag } from '../../dev/tracing.js';
/**
* @typedef {{
@ -82,6 +85,8 @@ export class Boundary {
#pending_count = 0;
#is_creating_fallback = false;
effect_pending = source(0);
/**
* @param {TemplateNode} node
* @param {BoundaryProps} props
@ -98,6 +103,10 @@ export class Boundary {
this.pending = !!this.#props.pending;
if (DEV) {
tag(this.effect_pending, '$effect.pending()');
}
this.#effect = block(() => {
/** @type {Effect} */ (active_effect).b = this;
@ -210,19 +219,26 @@ export class Boundary {
}
}
increment() {
this.#pending_count++;
}
/** @param {1 | -1} d */
#update_pending_count(d) {
this.#pending_count += d;
decrement() {
if (--this.#pending_count === 0) {
if (this.#pending_count === 0) {
this.commit();
}
}
if (this.#main_effect !== null) {
// TODO do we also need to `resume_effect` here?
// schedule_effect(this.#main_effect);
}
/** @param {1 | -1} d */
update_pending_count(d) {
if (this.has_pending_snippet()) {
this.#update_pending_count(d);
} else if (this.parent) {
this.parent.#update_pending_count(d);
}
queueMicrotask(() => {
update(this.effect_pending, d);
});
}
/** @param {unknown} error */
@ -373,10 +389,10 @@ export function capture(track = true) {
export function suspend() {
let boundary = get_pending_boundary();
boundary.increment();
boundary.update_pending_count(1);
return function unsuspend() {
boundary.decrement();
boundary.update_pending_count(-1);
};
}
@ -401,3 +417,14 @@ function exit() {
set_active_reaction(null);
set_component_context(null);
}
export function pending() {
// TODO throw helpful error if called outside an effect
const boundary = /** @type {Effect} */ (active_effect).b;
if (boundary === null) {
return 0; // TODO eventually we will need this to be global
}
return get(boundary.effect_pending);
}

@ -115,15 +115,7 @@ export {
user_effect,
user_pre_effect
} from './reactivity/effects.js';
export {
mutable_source,
mutate,
pending,
set,
state,
update,
update_pre
} from './reactivity/sources.js';
export { mutable_source, mutate, set, state, update, update_pre } from './reactivity/sources.js';
export {
prop,
rest_props,
@ -143,7 +135,7 @@ export {
update_store,
mark_store_binding
} from './reactivity/store.js';
export { boundary, save, suspend } from './dom/blocks/boundary.js';
export { boundary, pending, save, suspend } from './dom/blocks/boundary.js';
export { set_text } from './render.js';
export {
get,

@ -10,8 +10,6 @@ import {
set_signal_status,
update_effect
} from '../runtime.js';
import { raf } from '../timing.js';
import { internal_set, pending } from './sources.js';
/** @type {Set<Batch>} */
const batches = new Set();
@ -19,11 +17,6 @@ const batches = new Set();
/** @type {Batch | null} */
export let current_batch = null;
/** Update `$effect.pending()` */
function update_pending() {
internal_set(pending, batches.size > 0);
}
/** @type {Map<Derived, any> | null} */
export let batch_deriveds = null;
@ -239,8 +232,6 @@ export class Batch {
}
this.#callbacks.clear();
raf.tick(update_pending);
}
increment() {
@ -295,10 +286,6 @@ export class Batch {
static ensure() {
if (current_batch === null) {
if (batches.size === 0) {
raf.tick(update_pending);
}
const batch = (current_batch = new Batch());
batches.add(current_batch);
@ -315,3 +302,13 @@ export class Batch {
return current_batch;
}
}
/**
* Forcibly remove all current batches
* TODO investigate why we need this in tests
*/
export function clear() {
for (const batch of batches) {
batch.remove();
}
}

@ -31,7 +31,7 @@ import { destroy_effect, render_effect } from './effects.js';
import { inspect_effects, internal_set, set_inspect_effects, source } from './sources.js';
import { get_stack } from '../dev/tracing.js';
import { tracing_mode_flag } from '../../flags/index.js';
import { get_pending_boundary } from '../dom/blocks/boundary.js';
import { Boundary, get_pending_boundary } from '../dom/blocks/boundary.js';
import { component_context } from '../context.js';
import { UNINITIALIZED } from '../../../constants.js';
import { current_batch } from './batch.js';
@ -105,7 +105,7 @@ export function async_derived(fn, location) {
throw new Error('TODO cannot create unowned async derived');
}
let boundary = get_pending_boundary();
var boundary = /** @type {Boundary} */ (parent.b);
var promise = /** @type {Promise<V>} */ (/** @type {unknown} */ (undefined));
var signal = source(/** @type {V} */ (UNINITIALIZED));
@ -135,7 +135,8 @@ export function async_derived(fn, location) {
var pending = boundary.pending;
if (should_suspend) {
(pending ? boundary : batch).increment();
boundary.update_pending_count(1);
if (!pending) batch.increment();
}
/**
@ -148,7 +149,8 @@ export function async_derived(fn, location) {
from_async_derived = null;
if (should_suspend) {
(pending ? boundary : batch).decrement();
boundary.update_pending_count(-1);
if (!pending) batch.decrement();
}
if (!pending) batch.restore();

@ -43,9 +43,6 @@ export let inspect_effects = new Set();
/** @type {Map<Source, any>} */
export const old_values = new Map();
/** Internal representation of `$effect.pending()` */
export let pending = source(false);
/**
* @param {Set<any>} v
*/

@ -11,6 +11,7 @@ import { assert_html_equal, assert_html_equal_with_options } from '../html_equal
import { raf } from '../animation-helpers.js';
import type { CompileOptions } from '#compiler';
import { suite_with_variants, type BaseTest } from '../suite.js';
import { clear } from '../../src/internal/client/reactivity/batch.js';
type Assert = typeof import('vitest').assert & {
htmlEqual(a: string, b: string, description?: string): void;
@ -521,6 +522,8 @@ async function run_test_variant(
console.log = console_log;
console.warn = console_warn;
console.error = console_error;
clear();
}
}

Loading…
Cancel
Save