WIP getAbortSignal

pull/16266/head
Rich Harris 3 months ago
parent e896c55b6c
commit bc10186b69

@ -1,7 +1,7 @@
/** @import { ComponentContext, ComponentContextLegacy } from '#client' */
/** @import { EventDispatcher } from './index.js' */
/** @import { NotFunction } from './internal/types.js' */
import { untrack } from './internal/client/runtime.js';
import { active_reaction, untrack } from './internal/client/runtime.js';
import { is_array } from './internal/shared/utils.js';
import { user_effect } from './internal/client/index.js';
import * as e from './internal/client/errors.js';
@ -44,6 +44,37 @@ if (DEV) {
throw_rune_error('$bindable');
}
/**
* Returns an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that aborts when the current [derived](https://svelte.dev/docs/svelte/$derived) or [effect](https://svelte.dev/docs/svelte/$effect) re-runs or is destroyed.
*
* Must be called while a derived or effect is running.
*
* ```svelte
* <script>
* import { getAbortSignal } from 'svelte';
*
* let { id } = $props();
*
* async function getData(id) {
* const response = await fetch(`/items/${id}`, {
* signal: getAbortSignal()
* });
*
* return await response.json();
* }
*
* const data = $derived(await getData(id));
* </script>
* ```
*/
export function getAbortSignal() {
if (active_reaction === null) {
throw new Error('TODO getAbortSignal can only be called inside a reaction');
}
return (active_reaction.ac ??= new AbortController()).signal;
}
/**
* `onMount`, like [`$effect`](https://svelte.dev/docs/svelte/$effect), schedules a function to run as soon as the component has been mounted to the DOM.
* Unlike `$effect`, the provided function only runs once.

@ -35,6 +35,21 @@ export function unmount() {
export async function tick() {}
/** @type {AbortController | null} */
let controller = null;
export function getAbortSignal() {
if (controller === null) {
const c = (controller = new AbortController());
queueMicrotask(() => {
c.abort();
controller = null;
});
}
return controller.signal;
}
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';
export { createRawSnippet } from './internal/server/blocks/snippet.js';

@ -27,6 +27,8 @@ export const LEGACY_PROPS = Symbol('legacy props');
export const LOADING_ATTR_SYMBOL = Symbol('');
export const PROXY_PATH_SYMBOL = Symbol('proxy path');
export const STALE_REACTION = Symbol('stale reaction');
export const ELEMENT_NODE = 1;
export const TEXT_NODE = 3;
export const COMMENT_NODE = 8;

@ -32,7 +32,8 @@ import {
HEAD_EFFECT,
MAYBE_DIRTY,
EFFECT_HAS_DERIVED,
BOUNDARY_EFFECT
BOUNDARY_EFFECT,
STALE_REACTION
} from '#client/constants';
import { set } from './sources.js';
import * as e from '../errors.js';
@ -397,6 +398,8 @@ export function destroy_effect_children(signal, remove_dom = false) {
signal.first = signal.last = null;
while (effect !== null) {
effect.ac?.abort(STALE_REACTION);
var next = effect.next;
if ((effect.f & ROOT_EFFECT) !== 0) {
@ -478,6 +481,7 @@ export function destroy_effect(effect, remove_dom = true) {
effect.fn =
effect.nodes_start =
effect.nodes_end =
effect.ac =
null;
}

@ -40,6 +40,8 @@ export interface Reaction extends Signal {
fn: null | Function;
/** Signals that this signal reads from */
deps: null | Value[];
/** An AbortController that aborts when the signal is destroyed */
ac: null | AbortController;
}
export interface Derived<V = unknown> extends Value<V>, Reaction {

@ -22,7 +22,8 @@ import {
ROOT_EFFECT,
LEGACY_DERIVED_PROP,
DISCONNECTED,
EFFECT_IS_UPDATING
EFFECT_IS_UPDATING,
STALE_REACTION
} from './constants.js';
import { flush_tasks } from './dom/task.js';
import { internal_set, old_values } from './reactivity/sources.js';
@ -276,6 +277,11 @@ export function update_reaction(reaction) {
reaction.f |= EFFECT_IS_UPDATING;
if (reaction.ac !== null) {
reaction.ac?.abort(STALE_REACTION);
reaction.ac = null;
}
try {
var result = /** @type {Function} */ (0, reaction.fn)();
var deps = reaction.deps;

Loading…
Cancel
Save