mirror of https://github.com/sveltejs/svelte
parent
a2cbfe2b15
commit
72f30dd892
@ -0,0 +1,129 @@
|
||||
/** @import { Derived, Effect, Fork, Value } from '#client' */
|
||||
import { BLOCK_EFFECT, DERIVED, DIRTY, TEMPLATE_EFFECT } from './constants.js';
|
||||
import { queue_micro_task } from './dom/task.js';
|
||||
import { flush_sync, schedule_effect, set_signal_status } from './runtime.js';
|
||||
|
||||
/** @type {Fork | null} */
|
||||
export let active_fork = null;
|
||||
|
||||
/**
|
||||
* @param {Fork | null} fork
|
||||
*/
|
||||
export function set_active_fork(fork) {
|
||||
active_fork = fork;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(error?: Error) => void} callback
|
||||
* @returns {Fork}
|
||||
*/
|
||||
function create_fork(callback) {
|
||||
return {
|
||||
pending: 0,
|
||||
sources: new Map(),
|
||||
callback
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Fork} fork
|
||||
*/
|
||||
export function increment_fork(fork) {
|
||||
fork.pending += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Fork} fork
|
||||
*/
|
||||
export function decrement_fork(fork) {
|
||||
fork.pending -= 1;
|
||||
|
||||
queue_micro_task(() => {
|
||||
if (fork.pending === 0) {
|
||||
// TODO if the state that was originally set inside the
|
||||
// fork callback was updated in the meantime, reject
|
||||
// the fork. Also need to handle a case like
|
||||
// `{#if a || b}` where the fork makes `a`
|
||||
// truthy but `b` became truthy in the
|
||||
// meantime — requires a 'rebase'
|
||||
fork.callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Fork} fork
|
||||
*/
|
||||
function apply_fork(fork) {
|
||||
// TODO check the fork is still valid and error otherwise
|
||||
|
||||
for (const [source, saved] of fork.sources) {
|
||||
source.v = saved.next_v;
|
||||
source.wv = saved.next_wv;
|
||||
|
||||
mark_effects(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Value} value
|
||||
*/
|
||||
function mark_effects(value) {
|
||||
if (value.reactions === null) return;
|
||||
|
||||
for (var reaction of value.reactions) {
|
||||
var flags = reaction.f;
|
||||
|
||||
if ((flags & DERIVED) !== 0) {
|
||||
mark_effects(/** @type {Derived} */ (reaction));
|
||||
} else {
|
||||
if ((flags & BLOCK_EFFECT) === 0 || (flags & TEMPLATE_EFFECT) !== 0) {
|
||||
set_signal_status(reaction, DIRTY);
|
||||
schedule_effect(/** @type {Effect} */ (reaction));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Fork} fork
|
||||
*/
|
||||
function revert(fork) {
|
||||
for (const [source, saved] of fork.sources) {
|
||||
source.v = saved.v;
|
||||
source.wv = saved.wv;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void} fn
|
||||
* @returns {Promise<{ apply: () => void }>}
|
||||
*/
|
||||
export function fork(fn) {
|
||||
flush_sync();
|
||||
|
||||
if (active_fork !== null) {
|
||||
throw new Error("TODO can't fork inside a fork, you mad fool");
|
||||
}
|
||||
|
||||
try {
|
||||
return new Promise((fulfil, reject) => {
|
||||
var f = (active_fork = create_fork((error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
fulfil({
|
||||
apply: () => apply_fork(f)
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
fn();
|
||||
flush_sync();
|
||||
|
||||
revert(active_fork);
|
||||
});
|
||||
} finally {
|
||||
active_fork = null;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue