diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 40ad60b510..c1c4bfdc6a 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -93,7 +93,7 @@ export function set_current_effect(effect) { /** @type {null | import('#client').Value[]} */ export let current_dependencies = null; -let current_dependencies_index = 0; + /** * Tracks writes that the effect it's executed in doesn't listen to yet, * so that the dependency can be added to the effect later on if it then reads it @@ -334,88 +334,61 @@ function handle_error(error, effect, component_context) { /** * @template V - * @param {import('#client').Reaction} signal + * @param {import('#client').Reaction} reaction * @returns {V} */ -export function execute_reaction_fn(signal) { +export function execute_reaction_fn(reaction) { const previous_dependencies = current_dependencies; - const previous_dependencies_index = current_dependencies_index; const previous_untracked_writes = current_untracked_writes; const previous_reaction = current_reaction; const previous_skip_reaction = current_skip_reaction; current_dependencies = /** @type {null | import('#client').Value[]} */ (null); - current_dependencies_index = 0; current_untracked_writes = null; - current_reaction = (signal.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? signal : null; - current_skip_reaction = !is_flushing_effect && (signal.f & UNOWNED) !== 0; + current_reaction = (reaction.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; + current_skip_reaction = !is_flushing_effect && (reaction.f & UNOWNED) !== 0; try { - let res = /** @type {Function} */ (0, signal.fn)(); - let dependencies = /** @type {import('#client').Value[]} **/ (signal.deps); - if (current_dependencies !== null) { - let i; - if (dependencies !== null) { - const deps_length = dependencies.length; - // Include any dependencies up until the current_dependencies_index. - const full_current_dependencies = - current_dependencies_index === 0 - ? current_dependencies - : dependencies.slice(0, current_dependencies_index).concat(current_dependencies); - const current_dep_length = full_current_dependencies.length; - // If we have more than 16 elements in the array then use a Set for faster performance - // TODO: evaluate if we should always just use a Set or not here? - const full_current_dependencies_set = - current_dep_length > 16 && deps_length - current_dependencies_index > 1 - ? new Set(full_current_dependencies) - : null; - for (i = current_dependencies_index; i < deps_length; i++) { - const dependency = dependencies[i]; - if ( - full_current_dependencies_set !== null - ? !full_current_dependencies_set.has(dependency) - : !full_current_dependencies.includes(dependency) - ) { - remove_reaction(signal, dependency); - } - } + let result = /** @type {Function} */ (0, reaction.fn)(); + + let old_deps = reaction.deps; + var start = 0; + var i; + var dependency; + + // very often, dependencies stay the same between runs — optimise for that case + // by skipping the first n identical dependencies + if (current_dependencies !== null && old_deps !== null) { + var min = Math.min(current_dependencies.length, old_deps.length); + + for (; start < min; start += 1) { + if (current_dependencies[start] !== old_deps[start]) break; } + } - if (dependencies !== null && current_dependencies_index > 0) { - dependencies.length = current_dependencies_index + current_dependencies.length; - for (i = 0; i < current_dependencies.length; i++) { - dependencies[current_dependencies_index + i] = current_dependencies[i]; + if (old_deps !== null) { + for (i = start; i < old_deps.length; i += 1) { + dependency = old_deps[i]; + if (current_dependencies === null || !current_dependencies.includes(dependency)) { + remove_reaction(reaction, dependency); } - } else { - signal.deps = /** @type {import('#client').Value[]} **/ ( - dependencies = current_dependencies - ); } + } - if (!current_skip_reaction) { - for (i = current_dependencies_index; i < dependencies.length; i++) { - const dependency = dependencies[i]; - const reactions = dependency.reactions; - - if (reactions === null) { - dependency.reactions = [signal]; - } else if (reactions[reactions.length - 1] !== signal) { - // TODO: should this be: - // - // } else if (!reactions.includes(signal)) { - // - reactions.push(signal); - } + if (current_dependencies !== null && !current_skip_reaction) { + for (i = start; i < current_dependencies.length; i += 1) { + dependency = current_dependencies[i]; + if (old_deps === null || !old_deps.includes(dependency)) { + (dependency.reactions ??= []).push(reaction); } } - } else if (dependencies !== null && current_dependencies_index < dependencies.length) { - remove_reactions(signal, current_dependencies_index); - dependencies.length = current_dependencies_index; } - return res; + + reaction.deps = current_dependencies; + + return result; } finally { current_dependencies = previous_dependencies; - current_dependencies_index = previous_dependencies_index; current_untracked_writes = previous_untracked_writes; current_reaction = previous_reaction; current_skip_reaction = previous_skip_reaction; @@ -810,26 +783,10 @@ export function get(signal) { // Register the dependency on the current reaction signal. if (current_reaction !== null) { - const unowned = (current_reaction.f & UNOWNED) !== 0; - const dependencies = current_reaction.deps; - if ( - current_dependencies === null && - dependencies !== null && - dependencies[current_dependencies_index] === signal && - !(unowned && current_effect !== null) - ) { - current_dependencies_index++; - } else if ( - dependencies === null || - current_dependencies_index === 0 || - dependencies[current_dependencies_index - 1] !== signal - ) { - if (current_dependencies === null) { - current_dependencies = [signal]; - } else if (current_dependencies[current_dependencies.length - 1] !== signal) { - current_dependencies.push(signal); - } + if (!current_dependencies?.includes(signal)) { + (current_dependencies ??= []).push(signal); } + if ( current_untracked_writes !== null && current_effect !== null &&