expand on effect sequencing with levels

levels
Dominic Gannaway 4 months ago
parent 924f0611f7
commit eb7ba0d0a2

@ -5,7 +5,6 @@ import {
current_effect,
current_reaction,
destroy_children,
flush_local_render_effects,
get,
remove_reactions,
schedule_effect,
@ -15,15 +14,31 @@ import {
import { DIRTY, MANAGED, RENDER_EFFECT, EFFECT, PRE_EFFECT, DESTROYED } from '../constants.js';
import { set } from './sources.js';
export let current_pre_effect_sibling_count = 0;
export let current_render_effect_sibling_count = 0;
export let current_effect_sibling_count = 0;
/**
* @param {number} pre
* @param {number} render
* @param {number} effect
*/
export function set_sibling_count(pre, render, effect) {
current_pre_effect_sibling_count = pre;
current_render_effect_sibling_count = render;
current_effect_sibling_count = effect;
}
/**
* @param {import('./types.js').EffectType} type
* @param {(() => void | (() => void)) | ((b: import('#client').Block) => void | (() => void))} fn
* @param {boolean} sync
* @param {number} sibling_count
* @param {null | import('#client').Block} block
* @param {boolean} init
* @returns {import('#client').Effect}
*/
function create_effect(type, fn, sync, block = current_block, init = true) {
function create_effect(type, fn, sync, sibling_count, block = current_block, init = true) {
/** @type {import('#client').Effect} */
const signal = {
block,
@ -39,7 +54,11 @@ function create_effect(type, fn, sync, block = current_block, init = true) {
};
if (current_effect !== null) {
signal.l = current_effect.l + 1;
// 256 per effect level
const level = current_effect.l;
const mod = level % 256;
const next_level = (level - mod) + 256 + sibling_count;
signal.l = next_level;
}
if ((type & MANAGED) === 0) {
@ -88,7 +107,7 @@ export function user_effect(fn) {
current_component_context !== null &&
!current_component_context.m;
const effect = create_effect(EFFECT, fn, false, current_block, !defer);
const effect = create_effect(EFFECT, fn, false, current_effect_sibling_count++, current_block, !defer);
if (defer) {
const context = /** @type {import('#client').ComponentContext} */ (current_component_context);
@ -115,7 +134,7 @@ export function user_root_effect(fn) {
* @returns {import('#client').Effect}
*/
export function effect(fn) {
return create_effect(EFFECT, fn, false);
return create_effect(EFFECT, fn, false, current_effect_sibling_count++);
}
/**
@ -123,7 +142,7 @@ export function effect(fn) {
* @returns {import('#client').Effect}
*/
export function managed_effect(fn) {
return create_effect(EFFECT | MANAGED, fn, false);
return create_effect(EFFECT | MANAGED, fn, false, current_effect_sibling_count++);
}
/**
@ -131,7 +150,7 @@ export function managed_effect(fn) {
* @returns {import('#client').Effect}
*/
export function managed_pre_effect(fn) {
return create_effect(PRE_EFFECT | MANAGED, fn, false);
return create_effect(PRE_EFFECT | MANAGED, fn, false, current_pre_effect_sibling_count++);
}
/**
@ -150,7 +169,7 @@ export function pre_effect(fn) {
}
const sync = current_effect !== null && (current_effect.f & RENDER_EFFECT) !== 0;
return create_effect(PRE_EFFECT, fn, sync);
return create_effect(PRE_EFFECT, fn, sync, current_pre_effect_sibling_count++);
}
/**
@ -175,7 +194,8 @@ export function legacy_pre_effect(deps, fn) {
set(component_context.l2, true);
return untrack(fn);
},
true
true,
current_pre_effect_sibling_count++
);
}
@ -200,7 +220,7 @@ export function legacy_pre_effect_reset() {
* @returns {import('#client').Effect}
*/
export function invalidate_effect(fn) {
return create_effect(PRE_EFFECT, fn, true);
return create_effect(PRE_EFFECT, fn, true, current_pre_effect_sibling_count++);
}
/**
@ -216,7 +236,7 @@ export function render_effect(fn, block = current_block, managed = false, sync =
if (managed) {
flags |= MANAGED;
}
return create_effect(flags, /** @type {any} */ (fn), sync, block);
return create_effect(flags, /** @type {any} */ (fn), sync, current_render_effect_sibling_count++, block);
}
/**

@ -8,7 +8,14 @@ import {
object_prototype
} from './utils.js';
import { unstate } from './proxy.js';
import { destroy_effect, pre_effect } from './reactivity/effects.js';
import {
current_effect_sibling_count,
current_pre_effect_sibling_count,
current_render_effect_sibling_count,
destroy_effect,
pre_effect,
set_sibling_count
} from './reactivity/effects.js';
import {
EACH_BLOCK,
IF_BLOCK,
@ -384,12 +391,19 @@ export function execute_effect(signal) {
const previous_effect = current_effect;
const previous_component_context = current_component_context;
const previous_block = current_block;
const previous_pre_effect_sibling_count = current_pre_effect_sibling_count;
const previous_render_effect_sibling_count = current_render_effect_sibling_count;
const previous_effect_sibling_count = current_effect_sibling_count;
const component_context = signal.ctx;
current_effect = signal;
current_component_context = component_context;
current_block = signal.block;
// 32 for pre
// 128 for render
// 96 for effect
set_sibling_count(0, 32, 160);
try {
destroy_children(signal);
@ -400,6 +414,11 @@ export function execute_effect(signal) {
current_effect = previous_effect;
current_component_context = previous_component_context;
current_block = previous_block;
set_sibling_count(
previous_pre_effect_sibling_count,
previous_render_effect_sibling_count,
previous_effect_sibling_count
);
}
if ((signal.f & PRE_EFFECT) !== 0 && current_queued_pre_and_render_effects.length > 0) {
@ -510,20 +529,26 @@ export function schedule_effect(signal, sync) {
if (!should_append) {
const target_level = signal.l;
const target_group_level = target_level - (target_level % 256);
const target_block = signal.block;
const is_pre_effect = (flags & PRE_EFFECT) !== 0;
let target_signal;
let target_signal_level;
let target_signal_group_level;
let is_target_pre_effect;
let i = length;
while (true) {
target_signal = current_queued_pre_and_render_effects[--i];
target_signal_level = target_signal.l;
target_signal_group_level = target_signal_level - (target_signal_level % 256);
if (target_group_level === target_signal_group_level && target_signal.block !== target_block) {
current_queued_pre_and_render_effects.splice(i + 1, 0, signal);
break;
}
if (target_signal_level <= target_level) {
if (i + 1 === length) {
should_append = true;
} else {
is_target_pre_effect = (target_signal.f & PRE_EFFECT) !== 0;
if (
target_signal_level < target_level ||
target_signal.block !== target_block ||

@ -30,7 +30,7 @@ test('set.values()', () => {
set.clear();
});
assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, [], false]); // TODO update when we fix effect ordering bug
assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, false, []]);
cleanup();
});

@ -1,3 +1,4 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
// https://github.com/sveltejs/svelte/issues/7884
@ -27,11 +28,22 @@ export default test({
const event = new window.Event('change');
inputs[0].checked = true;
await inputs[0].dispatchEvent(event);
flushSync(() => {
inputs[0].dispatchEvent(event);
});
inputs[2].checked = true;
await inputs[2].dispatchEvent(event);
flushSync(() => {
inputs[2].dispatchEvent(event);
});
inputs[3].checked = true;
await inputs[3].dispatchEvent(event);
flushSync(() => {
inputs[3].dispatchEvent(event);
});
assert.htmlEqual(
target.innerHTML,
@ -53,7 +65,8 @@ export default test({
);
await component.update();
await Promise.resolve();
flushSync();
assert.htmlEqual(
target.innerHTML,

@ -1,3 +1,4 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
@ -11,7 +12,10 @@ export default test({
const buttons = target.querySelectorAll('button');
await buttons[0].dispatchEvent(event);
flushSync(() => {
buttons[0].dispatchEvent(event);
});
assert.htmlEqual(
target.innerHTML,
`
@ -20,7 +24,10 @@ export default test({
`
);
await buttons[1].dispatchEvent(event);
flushSync(() => {
buttons[1].dispatchEvent(event);
});
assert.htmlEqual(
target.innerHTML,
`
@ -30,7 +37,10 @@ export default test({
);
// reactive update, reset to 2
await buttons[0].dispatchEvent(event);
flushSync(() => {
buttons[0].dispatchEvent(event);
});
assert.htmlEqual(
target.innerHTML,
`
@ -40,7 +50,10 @@ export default test({
);
// bound to main, reset to 2
await buttons[1].dispatchEvent(event);
flushSync(() => {
buttons[1].dispatchEvent(event);
});
assert.htmlEqual(
target.innerHTML,
`

@ -1,3 +1,4 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@ -11,7 +12,9 @@ export default test({
const button = target.querySelector('button');
ok(button);
await button.dispatchEvent(click);
flushSync(() => {
button.dispatchEvent(click);
});
assert.equal(component.x, 2);
assert.htmlEqual(
@ -22,7 +25,9 @@ export default test({
`
);
await button.dispatchEvent(click);
flushSync(() => {
button.dispatchEvent(click);
});
assert.equal(component.x, 6);
assert.htmlEqual(

@ -1,3 +1,4 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';
import { reset_numbers } from './data';
@ -18,7 +19,9 @@ export default test({
const clickEvent = new window.MouseEvent('click', { bubbles: true });
await btn.dispatchEvent(clickEvent);
flushSync(() => {
btn.dispatchEvent(clickEvent);
});
assert.htmlEqual(
target.innerHTML,
@ -31,7 +34,9 @@ export default test({
`
);
await btn.dispatchEvent(clickEvent);
flushSync(() => {
btn.dispatchEvent(clickEvent);
});
assert.htmlEqual(
target.innerHTML,

@ -1,3 +1,4 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
@ -13,7 +14,9 @@ export default test({
assert.equal(component.x, 1);
await button.dispatchEvent(click);
flushSync(() => {
button.dispatchEvent(click);
});
assert.equal(component.x, 3);
assert.htmlEqual(
@ -24,7 +27,9 @@ export default test({
`
);
await button.dispatchEvent(click);
flushSync(() => {
button.dispatchEvent(click);
});
assert.equal(component.x, 5);
assert.htmlEqual(

Loading…
Cancel
Save