Skip to content

Commit

Permalink
chore: optimise effects (#11569)
Browse files Browse the repository at this point in the history
* optimise effects

* tweak

* don't allocate array unnecessarily

* filter_flags appears to be unnecessary?

* lint

* tweak

* simplify (#11570)

* changeset
  • Loading branch information
Rich-Harris committed May 12, 2024
1 parent 5497b3d commit 9c680f1
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 77 deletions.
5 changes: 5 additions & 0 deletions .changeset/blue-waves-sneeze.md
@@ -0,0 +1,5 @@
---
"svelte": patch
---

chore: optimise effects
17 changes: 1 addition & 16 deletions packages/svelte/src/internal/client/dom/legacy/lifecycle.js
@@ -1,14 +1,6 @@
import { CLEAN } from '../../constants.js';
import { run, run_all } from '../../../shared/utils.js';
import { user_pre_effect, user_effect } from '../../reactivity/effects.js';
import {
current_component_context,
current_effect,
deep_read_state,
flush_local_render_effects,
get,
untrack
} from '../../runtime.js';
import { current_component_context, deep_read_state, get, untrack } from '../../runtime.js';

/**
* Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects
Expand All @@ -26,13 +18,6 @@ export function init() {
user_pre_effect(() => {
observe_all(context);
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);
}
});
}

Expand Down
92 changes: 31 additions & 61 deletions packages/svelte/src/internal/client/runtime.js
Expand Up @@ -521,9 +521,27 @@ function infinite_loop_guard() {
*/
function flush_queued_root_effects(root_effects) {
infinite_loop_guard();
for (var i = 0; i < root_effects.length; i++) {
var signal = root_effects[i];
flush_nested_effects(signal, RENDER_EFFECT | EFFECT);

var previously_flushing_effect = is_flushing_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;
}
}

Expand Down Expand Up @@ -592,12 +610,10 @@ export function schedule_effect(signal) {
* effects to be flushed.
*
* @param {import('#client').Effect} effect
* @param {number} filter_flags
* @param {boolean} shallow
* @param {import('#client').Effect[]} collected_effects
* @returns {void}
*/
function process_effects(effect, filter_flags, shallow, collected_effects) {
function process_effects(effect, collected_effects) {
var current_effect = effect.first;
var effects = [];

Expand All @@ -621,13 +637,14 @@ function process_effects(effect, filter_flags, shallow, collected_effects) {
// Child might have been mutated since running the effect
child = current_effect.first;
}
if (!shallow && child !== null) {

if (child !== null) {
current_effect = child;
continue;
}
} else if ((flags & EFFECT) !== 0) {
if (is_branch || is_clean) {
if (!shallow && child !== null) {
if (child !== null) {
current_effect = child;
continue;
}
Expand Down Expand Up @@ -657,62 +674,15 @@ function process_effects(effect, filter_flags, shallow, collected_effects) {
current_effect = sibling;
}

if (effects.length > 0) {
// 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.
for (var i = 0; i < effects.length; i++) {
if ((filter_flags & EFFECT) !== 0) {
collected_effects.push(effects[i]);
}
if (!shallow) {
process_effects(effects[i], filter_flags, false, collected_effects);
}
}
// 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.
for (var i = 0; i < effects.length; i++) {
child = effects[i];
collected_effects.push(child);
process_effects(child, 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.
* Returns the result of the passed function, if given.
Expand Down

0 comments on commit 9c680f1

Please sign in to comment.