|
|
|
|
@ -21,7 +21,7 @@ import {
|
|
|
|
|
USER_EFFECT
|
|
|
|
|
} from '#client/constants';
|
|
|
|
|
import { async_mode_flag } from '../../flags/index.js';
|
|
|
|
|
import { deferred, define_property, includes } from '../../shared/utils.js';
|
|
|
|
|
import { deferred, define_property } from '../../shared/utils.js';
|
|
|
|
|
import {
|
|
|
|
|
active_effect,
|
|
|
|
|
active_reaction,
|
|
|
|
|
@ -364,7 +364,6 @@ export class Batch {
|
|
|
|
|
} else if (async_mode_flag && (flags & (RENDER_EFFECT | MANAGED_EFFECT)) !== 0) {
|
|
|
|
|
render_effects.push(effect);
|
|
|
|
|
} else if (is_dirty(effect)) {
|
|
|
|
|
if ((flags & BLOCK_EFFECT) !== 0) this.#maybe_dirty_effects.add(effect);
|
|
|
|
|
update_effect(effect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -501,64 +500,6 @@ export class Batch {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#commit() {
|
|
|
|
|
// If there are other pending batches, they now need to be 'rebased' —
|
|
|
|
|
// in other words, we re-run block/async effects with the newly
|
|
|
|
|
// committed state, unless the batch in question has a more
|
|
|
|
|
// recent value for a given source
|
|
|
|
|
for (const batch of batches) {
|
|
|
|
|
var is_earlier = batch.id < this.id;
|
|
|
|
|
|
|
|
|
|
/** @type {Source[]} */
|
|
|
|
|
var sources = [];
|
|
|
|
|
|
|
|
|
|
for (const [source, value] of this.current) {
|
|
|
|
|
if (batch.current.has(source)) {
|
|
|
|
|
if (is_earlier && value !== batch.current.get(source)) {
|
|
|
|
|
// bring the value up to date
|
|
|
|
|
batch.current.set(source, value);
|
|
|
|
|
} else {
|
|
|
|
|
// same value or later batch has more recent value,
|
|
|
|
|
// no need to re-run these effects
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sources.push(source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sources.length === 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re-run async/block effects that depend on distinct values changed in both batches
|
|
|
|
|
var others = [...batch.current.keys()].filter((s) => !this.current.has(s));
|
|
|
|
|
if (others.length > 0) {
|
|
|
|
|
batch.activate();
|
|
|
|
|
|
|
|
|
|
/** @type {Set<Value>} */
|
|
|
|
|
var marked = new Set();
|
|
|
|
|
|
|
|
|
|
/** @type {Map<Reaction, boolean>} */
|
|
|
|
|
var checked = new Map();
|
|
|
|
|
|
|
|
|
|
for (var source of sources) {
|
|
|
|
|
mark_effects(source, others, marked, checked);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (batch.#roots.length > 0) {
|
|
|
|
|
batch.apply();
|
|
|
|
|
|
|
|
|
|
for (var root of batch.#roots) {
|
|
|
|
|
batch.#traverse(root, [], []);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO do we need to do anything with the dummy effect arrays?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
batch.deactivate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const { on_discard } of this.#skipped_branches.values()) {
|
|
|
|
|
on_discard(this);
|
|
|
|
|
}
|
|
|
|
|
@ -1031,37 +972,6 @@ function flush_queued_effects(effects) {
|
|
|
|
|
eager_block_effects = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is similar to `mark_reactions`, but it only marks async/block effects
|
|
|
|
|
* depending on `value` and at least one of the other `sources`, so that
|
|
|
|
|
* these effects can re-run after another batch has been committed
|
|
|
|
|
* @param {Value} value
|
|
|
|
|
* @param {Source[]} sources
|
|
|
|
|
* @param {Set<Value>} marked
|
|
|
|
|
* @param {Map<Reaction, boolean>} checked
|
|
|
|
|
*/
|
|
|
|
|
function mark_effects(value, sources, marked, checked) {
|
|
|
|
|
if (marked.has(value)) return;
|
|
|
|
|
marked.add(value);
|
|
|
|
|
|
|
|
|
|
if (value.reactions !== null) {
|
|
|
|
|
for (const reaction of value.reactions) {
|
|
|
|
|
const flags = reaction.f;
|
|
|
|
|
|
|
|
|
|
if ((flags & DERIVED) !== 0) {
|
|
|
|
|
mark_effects(/** @type {Derived} */ (reaction), sources, marked, checked);
|
|
|
|
|
} else if (
|
|
|
|
|
(flags & (ASYNC | BLOCK_EFFECT)) !== 0 &&
|
|
|
|
|
(flags & DIRTY) === 0 &&
|
|
|
|
|
depends_on(reaction, sources, checked)
|
|
|
|
|
) {
|
|
|
|
|
set_signal_status(reaction, DIRTY);
|
|
|
|
|
schedule_effect(/** @type {Effect} */ (reaction));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When committing a fork, we need to trigger eager effects so that
|
|
|
|
|
* any `$state.eager(...)` expressions update immediately. This
|
|
|
|
|
@ -1084,33 +994,6 @@ function mark_eager_effects(value, effects) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Reaction} reaction
|
|
|
|
|
* @param {Source[]} sources
|
|
|
|
|
* @param {Map<Reaction, boolean>} checked
|
|
|
|
|
*/
|
|
|
|
|
function depends_on(reaction, sources, checked) {
|
|
|
|
|
const depends = checked.get(reaction);
|
|
|
|
|
if (depends !== undefined) return depends;
|
|
|
|
|
|
|
|
|
|
if (reaction.deps !== null) {
|
|
|
|
|
for (const dep of reaction.deps) {
|
|
|
|
|
if (includes.call(sources, dep)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((dep.f & DERIVED) !== 0 && depends_on(/** @type {Derived} */ (dep), sources, checked)) {
|
|
|
|
|
checked.set(/** @type {Derived} */ (dep), true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checked.set(reaction, false);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Effect} effect
|
|
|
|
|
* @returns {void}
|
|
|
|
|
@ -1283,6 +1166,14 @@ export function fork(fn) {
|
|
|
|
|
|
|
|
|
|
batch.is_fork = false;
|
|
|
|
|
|
|
|
|
|
// TODO seems like we don't need this, but maybe worth keeping?
|
|
|
|
|
// var existing_batch = [...batches].find(
|
|
|
|
|
// (b) => !b.is_fork && batch.current.keys().some((s) => b.current.has(s))
|
|
|
|
|
// );
|
|
|
|
|
// if (existing_batch) {
|
|
|
|
|
// Batch.merge(batch, existing_batch);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// apply changes and update write versions so deriveds see the change
|
|
|
|
|
for (var [source, value] of batch.current) {
|
|
|
|
|
source.v = value;
|
|
|
|
|
|