move signal init logic into create_effect

pull/10931/head
Rich Harris 2 years ago
parent ed2981d7b8
commit 5df01493a7

@ -7,8 +7,10 @@ import {
destroy_children, destroy_children,
execute_effect, execute_effect,
get, get,
is_flushing_effect,
remove_reactions, remove_reactions,
schedule_effect, schedule_effect,
set_is_flushing_effect,
set_signal_status, set_signal_status,
untrack untrack
} from '../runtime.js'; } from '../runtime.js';
@ -20,7 +22,9 @@ import {
PRE_EFFECT, PRE_EFFECT,
DESTROYED, DESTROYED,
INERT, INERT,
IS_ELSEIF IS_ELSEIF,
CLEAN,
EFFECT_RAN
} from '../constants.js'; } from '../constants.js';
import { set } from './sources.js'; import { set } from './sources.js';
import { noop } from '../../common.js'; import { noop } from '../../common.js';
@ -63,7 +67,19 @@ function create_effect(type, fn, sync, init = true) {
} }
if (init) { if (init) {
schedule_effect(signal, sync); if (sync) {
const previously_flushing_effect = is_flushing_effect;
try {
set_is_flushing_effect(true);
execute_effect(signal);
set_signal_status(signal, CLEAN);
signal.f |= EFFECT_RAN;
} finally {
set_is_flushing_effect(previously_flushing_effect);
}
} else {
schedule_effect(signal);
}
} }
return signal; return signal;

@ -135,7 +135,7 @@ export function set(signal, value) {
) { ) {
if (current_dependencies !== null && current_dependencies.includes(signal)) { if (current_dependencies !== null && current_dependencies.includes(signal)) {
set_signal_status(current_effect, DIRTY); set_signal_status(current_effect, DIRTY);
schedule_effect(current_effect, false); schedule_effect(current_effect);
} else { } else {
if (current_untracked_writes === null) { if (current_untracked_writes === null) {
set_current_untracked_writes([signal]); set_current_untracked_writes([signal]);

@ -36,7 +36,13 @@ const FLUSH_SYNC = 1;
let current_scheduler_mode = FLUSH_MICROTASK; let current_scheduler_mode = FLUSH_MICROTASK;
// Used for handling scheduling // Used for handling scheduling
let is_micro_task_queued = false; let is_micro_task_queued = false;
let is_flushing_effect = false; export let is_flushing_effect = false;
/** @param {boolean} value */
export function set_is_flushing_effect(value) {
is_flushing_effect = value;
}
// Used for $inspect // Used for $inspect
export let is_batching_effect = false; export let is_batching_effect = false;
let is_inspecting_signal = false; let is_inspecting_signal = false;
@ -466,85 +472,73 @@ function process_microtask() {
/** /**
* @param {import('./types.js').Effect} signal * @param {import('./types.js').Effect} signal
* @param {boolean} sync
* @returns {void} * @returns {void}
*/ */
export function schedule_effect(signal, sync) { export function schedule_effect(signal) {
const flags = signal.f; const flags = signal.f;
if (sync) {
const previously_flushing_effect = is_flushing_effect; if (current_scheduler_mode === FLUSH_MICROTASK) {
try { if (!is_micro_task_queued) {
is_flushing_effect = true; is_micro_task_queued = true;
execute_effect(signal); queueMicrotask(process_microtask);
set_signal_status(signal, CLEAN);
} finally {
is_flushing_effect = previously_flushing_effect;
} }
} else { }
if (current_scheduler_mode === FLUSH_MICROTASK) {
if (!is_micro_task_queued) { if ((flags & EFFECT) !== 0) {
is_micro_task_queued = true; current_queued_effects.push(signal);
queueMicrotask(process_microtask); // Prevent any nested user effects from potentially triggering
} // before this effect is scheduled. We know they will be destroyed
// so we can make them inert to avoid having to find them in the
// queue and remove them.
if ((flags & MANAGED) === 0) {
mark_subtree_children_inert(signal, true);
} }
if ((flags & EFFECT) !== 0) { } else {
current_queued_effects.push(signal); // We need to ensure we insert the signal in the right topological order. In other words,
// Prevent any nested user effects from potentially triggering // we need to evaluate where to insert the signal based off its level and whether or not it's
// before this effect is scheduled. We know they will be destroyed // a pre-effect and within the same block. By checking the signals in the queue in reverse order
// so we can make them inert to avoid having to find them in the // we can find the right place quickly. TODO: maybe opt to use a linked list rather than an array
// queue and remove them. // for these operations.
if ((flags & MANAGED) === 0) { const length = current_queued_pre_and_render_effects.length;
mark_subtree_children_inert(signal, true); let should_append = length === 0;
}
} else { if (!should_append) {
// We need to ensure we insert the signal in the right topological order. In other words, const target_level = signal.l;
// we need to evaluate where to insert the signal based off its level and whether or not it's const is_pre_effect = (flags & PRE_EFFECT) !== 0;
// a pre-effect and within the same block. By checking the signals in the queue in reverse order let target_signal;
// we can find the right place quickly. TODO: maybe opt to use a linked list rather than an array let target_signal_level;
// for these operations. let is_target_pre_effect;
const length = current_queued_pre_and_render_effects.length; let i = length;
let should_append = length === 0; while (true) {
target_signal = current_queued_pre_and_render_effects[--i];
if (!should_append) { target_signal_level = target_signal.l;
const target_level = signal.l; if (target_signal_level <= target_level) {
const is_pre_effect = (flags & PRE_EFFECT) !== 0; if (i + 1 === length) {
let target_signal; should_append = true;
let target_signal_level; } else {
let is_target_pre_effect; is_target_pre_effect = (target_signal.f & PRE_EFFECT) !== 0;
let i = length; if (
while (true) { target_signal_level < target_level ||
target_signal = current_queued_pre_and_render_effects[--i]; target_signal !== signal ||
target_signal_level = target_signal.l; (is_target_pre_effect && !is_pre_effect)
if (target_signal_level <= target_level) { ) {
if (i + 1 === length) { i++;
should_append = true;
} else {
is_target_pre_effect = (target_signal.f & PRE_EFFECT) !== 0;
if (
target_signal_level < target_level ||
target_signal !== signal ||
(is_target_pre_effect && !is_pre_effect)
) {
i++;
}
current_queued_pre_and_render_effects.splice(i, 0, signal);
} }
break; current_queued_pre_and_render_effects.splice(i, 0, signal);
}
if (i === 0) {
current_queued_pre_and_render_effects.unshift(signal);
break;
} }
break;
}
if (i === 0) {
current_queued_pre_and_render_effects.unshift(signal);
break;
} }
} }
}
if (should_append) { if (should_append) {
current_queued_pre_and_render_effects.push(signal); current_queued_pre_and_render_effects.push(signal);
}
} }
} }
signal.f |= EFFECT_RAN;
} }
/** /**
@ -696,7 +690,7 @@ export function get(signal) {
current_untracked_writes.includes(signal) current_untracked_writes.includes(signal)
) { ) {
set_signal_status(current_effect, DIRTY); set_signal_status(current_effect, DIRTY);
schedule_effect(current_effect, false); schedule_effect(current_effect);
} }
} }
@ -772,7 +766,7 @@ export function mark_subtree_inert(signal, inert) {
if (is_already_inert !== inert) { if (is_already_inert !== inert) {
signal.f ^= INERT; signal.f ^= INERT;
if (!inert && (flags & CLEAN) === 0) { if (!inert && (flags & CLEAN) === 0) {
schedule_effect(signal, false); schedule_effect(signal);
} }
} }
@ -819,7 +813,7 @@ export function mark_reactions(signal, to_status, force_schedule) {
force_schedule force_schedule
); );
} else { } else {
schedule_effect(/** @type {import('#client').Effect} */ (reaction), false); schedule_effect(/** @type {import('#client').Effect} */ (reaction));
} }
} }
} }
@ -1075,7 +1069,7 @@ export function pop(component) {
if (effects !== null) { if (effects !== null) {
context_stack_item.e = null; context_stack_item.e = null;
for (let i = 0; i < effects.length; i++) { for (let i = 0; i < effects.length; i++) {
schedule_effect(effects[i], false); schedule_effect(effects[i]);
} }
} }
current_component_context = context_stack_item.p; current_component_context = context_stack_item.p;

Loading…
Cancel
Save