|
|
|
|
@ -88,7 +88,9 @@ export let collected_effects = null;
|
|
|
|
|
export let legacy_updates = null;
|
|
|
|
|
|
|
|
|
|
var flush_count = 0;
|
|
|
|
|
var source_stacks = DEV ? new Set() : null;
|
|
|
|
|
|
|
|
|
|
/** @type {Set<Value>} */
|
|
|
|
|
var source_stacks = new Set();
|
|
|
|
|
|
|
|
|
|
let uid = 1;
|
|
|
|
|
|
|
|
|
|
@ -284,6 +286,14 @@ export class Batch {
|
|
|
|
|
infinite_loop_guard();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DEV) {
|
|
|
|
|
// track all the values that were updated during this flush,
|
|
|
|
|
// so that they can be reset afterwards
|
|
|
|
|
for (const value of this.current.keys()) {
|
|
|
|
|
source_stacks.add(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we only reschedule previously-deferred effects if we expect
|
|
|
|
|
// to be able to run them after processing the batch
|
|
|
|
|
if (!this.#is_deferred()) {
|
|
|
|
|
@ -345,29 +355,35 @@ export class Batch {
|
|
|
|
|
for (const [e, t] of this.#skipped_branches) {
|
|
|
|
|
reset_branch(e, t);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (this.#pending === 0) {
|
|
|
|
|
this.#unlink();
|
|
|
|
|
|
|
|
|
|
if (updates.length > 0) {
|
|
|
|
|
/** @type {Batch} */ (/** @type {unknown} */ (current_batch)).#process();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches.
|
|
|
|
|
this.#dirty_effects.clear();
|
|
|
|
|
this.#maybe_dirty_effects.clear();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// append/remove branches
|
|
|
|
|
for (const fn of this.#commit_callbacks) fn(this);
|
|
|
|
|
this.#commit_callbacks.clear();
|
|
|
|
|
// clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches.
|
|
|
|
|
this.#dirty_effects.clear();
|
|
|
|
|
this.#maybe_dirty_effects.clear();
|
|
|
|
|
|
|
|
|
|
previous_batch = this;
|
|
|
|
|
flush_queued_effects(render_effects);
|
|
|
|
|
flush_queued_effects(effects);
|
|
|
|
|
previous_batch = null;
|
|
|
|
|
// append/remove branches
|
|
|
|
|
for (const fn of this.#commit_callbacks) fn(this);
|
|
|
|
|
this.#commit_callbacks.clear();
|
|
|
|
|
|
|
|
|
|
this.#deferred?.resolve();
|
|
|
|
|
}
|
|
|
|
|
previous_batch = this;
|
|
|
|
|
flush_queued_effects(render_effects);
|
|
|
|
|
flush_queued_effects(effects);
|
|
|
|
|
previous_batch = null;
|
|
|
|
|
|
|
|
|
|
this.#deferred?.resolve();
|
|
|
|
|
|
|
|
|
|
var next_batch = /** @type {Batch | null} */ (/** @type {unknown} */ (current_batch));
|
|
|
|
|
|
|
|
|
|
if (this.linked && this.#pending === 0) {
|
|
|
|
|
this.#unlink();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Order matters here - we need to commit and THEN continue flushing new batches, not the other way around,
|
|
|
|
|
// else we could start flushing a new batch and then, if it has pending work, rebase it right afterwards, which is wrong.
|
|
|
|
|
// In sync mode flushSync can cause #commit to wrongfully think that there needs to be a rebase, so we only do it in async mode
|
|
|
|
|
@ -382,17 +398,16 @@ export class Batch {
|
|
|
|
|
// causing an effect and therefore a root to be scheduled again. We need to traverse the current batch
|
|
|
|
|
// once more in that case - most of the time this will just clean up dirty branches.
|
|
|
|
|
if (this.#roots.length > 0) {
|
|
|
|
|
const batch = (next_batch ??= this);
|
|
|
|
|
if (next_batch === null) {
|
|
|
|
|
next_batch = this;
|
|
|
|
|
this.#link();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const batch = next_batch;
|
|
|
|
|
batch.#roots.push(...this.#roots.filter((r) => !batch.#roots.includes(r)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (next_batch !== null) {
|
|
|
|
|
if (DEV) {
|
|
|
|
|
for (const source of this.current.keys()) {
|
|
|
|
|
/** @type {Set<Source>} */ (source_stacks).add(source);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_batch.#process();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -491,9 +506,11 @@ export class Batch {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flush() {
|
|
|
|
|
var source_stacks = DEV ? new Set() : null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (DEV) {
|
|
|
|
|
source_stacks.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is_processing = true;
|
|
|
|
|
current_batch = this;
|
|
|
|
|
|
|
|
|
|
@ -511,7 +528,7 @@ export class Batch {
|
|
|
|
|
old_values.clear();
|
|
|
|
|
|
|
|
|
|
if (DEV) {
|
|
|
|
|
for (const source of /** @type {Set<Source>} */ (source_stacks)) {
|
|
|
|
|
for (const source of source_stacks) {
|
|
|
|
|
source.updated = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -534,6 +551,8 @@ export class Batch {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#commit() {
|
|
|
|
|
this.#unlink();
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
@ -737,27 +756,14 @@ export class Batch {
|
|
|
|
|
static ensure() {
|
|
|
|
|
if (current_batch === null) {
|
|
|
|
|
const batch = (current_batch = new Batch());
|
|
|
|
|
batch.#link();
|
|
|
|
|
|
|
|
|
|
if (last_batch === null) {
|
|
|
|
|
first_batch = last_batch = batch;
|
|
|
|
|
} else {
|
|
|
|
|
last_batch.#next = batch;
|
|
|
|
|
batch.#prev = last_batch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_batch = batch;
|
|
|
|
|
|
|
|
|
|
if (!is_processing) {
|
|
|
|
|
if (!is_flushing_sync) {
|
|
|
|
|
queue_micro_task(() => {
|
|
|
|
|
if (batch.#started) {
|
|
|
|
|
// a flushSync happened in the meantime
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_processing && !is_flushing_sync) {
|
|
|
|
|
queue_micro_task(() => {
|
|
|
|
|
if (!batch.#started) {
|
|
|
|
|
batch.flush();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -865,6 +871,17 @@ export class Batch {
|
|
|
|
|
this.#roots.push(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#link() {
|
|
|
|
|
if (last_batch === null) {
|
|
|
|
|
first_batch = last_batch = this;
|
|
|
|
|
} else {
|
|
|
|
|
last_batch.#next = this;
|
|
|
|
|
this.#prev = last_batch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_batch = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#unlink() {
|
|
|
|
|
var prev = this.#prev;
|
|
|
|
|
var next = this.#next;
|
|
|
|
|
|