chore: optimise effects (#11569)

* optimise effects

* tweak

* don't allocate array unnecessarily

* filter_flags appears to be unnecessary?

* lint

* tweak

* simplify (#11570)

* changeset
pull/11564/head
Rich Harris 8 months ago committed by GitHub
parent 5497b3d0bc
commit 9c680f1030
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
chore: optimise effects

@ -1,14 +1,6 @@
import { CLEAN } from '../../constants.js';
import { run, run_all } from '../../../shared/utils.js'; import { run, run_all } from '../../../shared/utils.js';
import { user_pre_effect, user_effect } from '../../reactivity/effects.js'; import { user_pre_effect, user_effect } from '../../reactivity/effects.js';
import { import { current_component_context, deep_read_state, get, untrack } from '../../runtime.js';
current_component_context,
current_effect,
deep_read_state,
flush_local_render_effects,
get,
untrack
} from '../../runtime.js';
/** /**
* Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects * Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects
@ -26,13 +18,6 @@ export function init() {
user_pre_effect(() => { user_pre_effect(() => {
observe_all(context); observe_all(context);
run_all(callbacks.b); run_all(callbacks.b);
// beforeUpdate might change state that affects rendering, ensure the render effects following from it
// are batched up with the current run. Avoids for example child components rerunning when they're
// now hidden because beforeUpdate did set an if block to false.
const parent = current_effect?.parent;
if (parent != null && (parent.f & CLEAN) === 0) {
flush_local_render_effects(parent);
}
}); });
} }

@ -521,9 +521,27 @@ function infinite_loop_guard() {
*/ */
function flush_queued_root_effects(root_effects) { function flush_queued_root_effects(root_effects) {
infinite_loop_guard(); infinite_loop_guard();
for (var i = 0; i < root_effects.length; i++) {
var signal = root_effects[i]; var previously_flushing_effect = is_flushing_effect;
flush_nested_effects(signal, RENDER_EFFECT | EFFECT); is_flushing_effect = true;
try {
for (var i = 0; i < root_effects.length; i++) {
var effect = root_effects[i];
// When working with custom elements, the root effects might not have a root
if (effect.first === null && (effect.f & BRANCH_EFFECT) === 0) {
flush_queued_effects([effect]);
} else {
/** @type {import('#client').Effect[]} */
var collected_effects = [];
process_effects(effect, collected_effects);
flush_queued_effects(collected_effects);
}
}
} finally {
is_flushing_effect = previously_flushing_effect;
} }
} }
@ -592,12 +610,10 @@ export function schedule_effect(signal) {
* effects to be flushed. * effects to be flushed.
* *
* @param {import('#client').Effect} effect * @param {import('#client').Effect} effect
* @param {number} filter_flags
* @param {boolean} shallow
* @param {import('#client').Effect[]} collected_effects * @param {import('#client').Effect[]} collected_effects
* @returns {void} * @returns {void}
*/ */
function process_effects(effect, filter_flags, shallow, collected_effects) { function process_effects(effect, collected_effects) {
var current_effect = effect.first; var current_effect = effect.first;
var effects = []; var effects = [];
@ -621,13 +637,14 @@ function process_effects(effect, filter_flags, shallow, collected_effects) {
// Child might have been mutated since running the effect // Child might have been mutated since running the effect
child = current_effect.first; child = current_effect.first;
} }
if (!shallow && child !== null) {
if (child !== null) {
current_effect = child; current_effect = child;
continue; continue;
} }
} else if ((flags & EFFECT) !== 0) { } else if ((flags & EFFECT) !== 0) {
if (is_branch || is_clean) { if (is_branch || is_clean) {
if (!shallow && child !== null) { if (child !== null) {
current_effect = child; current_effect = child;
continue; continue;
} }
@ -657,62 +674,15 @@ function process_effects(effect, filter_flags, shallow, collected_effects) {
current_effect = sibling; current_effect = sibling;
} }
if (effects.length > 0) { // We might be dealing with many effects here, far more than can be spread into
// We might be dealing with many effects here, far more than can be spread into // an array push call (callstack overflow). So let's deal with each effect in a loop.
// an array push call (callstack overflow). So let's deal with each effect in a loop. for (var i = 0; i < effects.length; i++) {
for (var i = 0; i < effects.length; i++) { child = effects[i];
if ((filter_flags & EFFECT) !== 0) { collected_effects.push(child);
collected_effects.push(effects[i]); process_effects(child, collected_effects);
}
if (!shallow) {
process_effects(effects[i], filter_flags, false, collected_effects);
}
}
} }
} }
/**
*
* This function recursively collects effects in topological order from the starting effect passed in.
* Effects will be collected when they match the filtered bitwise flag passed in only. The collected
* array will be populated with all the effects.
*
* @param {import('#client').Effect} effect
* @param {number} filter_flags
* @param {boolean} [shallow]
* @returns {void}
*/
function flush_nested_effects(effect, filter_flags, shallow = false) {
/** @type {import('#client').Effect[]} */
var collected_effects = [];
var previously_flushing_effect = is_flushing_effect;
is_flushing_effect = true;
try {
// When working with custom elements, the root effects might not have a root
if (effect.first === null && (effect.f & BRANCH_EFFECT) === 0) {
flush_queued_effects([effect]);
} else {
process_effects(effect, filter_flags, shallow, collected_effects);
flush_queued_effects(collected_effects);
}
} finally {
is_flushing_effect = previously_flushing_effect;
}
}
/**
* @param {import('#client').Effect} effect
* @returns {void}
*/
export function flush_local_render_effects(effect) {
infinite_loop_guard();
// We are entering a new flush sequence, so ensure counter is reset.
flush_count = 0;
flush_nested_effects(effect, RENDER_EFFECT, true);
}
/** /**
* Internal version of `flushSync` with the option to not flush previous effects. * Internal version of `flushSync` with the option to not flush previous effects.
* Returns the result of the passed function, if given. * Returns the result of the passed function, if given.

Loading…
Cancel
Save