import { run_all } from './utils.js'; | |
import { current_component, set_current_component } from './lifecycle.js'; | |
export const dirty_components = []; | |
export const intros = { enabled: false }; | |
export const binding_callbacks = []; | |
let render_callbacks = []; | |
const flush_callbacks = []; | |
const resolved_promise = /* @__PURE__ */ Promise.resolve(); | |
let update_scheduled = false; | |
/** @returns {void} */ | |
export function schedule_update() { | |
if (!update_scheduled) { | |
update_scheduled = true; | |
resolved_promise.then(flush); | |
} | |
} | |
/** @returns {Promise<void>} */ | |
export function tick() { | |
schedule_update(); | |
return resolved_promise; | |
} | |
/** @returns {void} */ | |
export function add_render_callback(fn) { | |
render_callbacks.push(fn); | |
} | |
/** @returns {void} */ | |
export function add_flush_callback(fn) { | |
flush_callbacks.push(fn); | |
} | |
// flush() calls callbacks in this order: | |
// 1. All beforeUpdate callbacks, in order: parents before children | |
// 2. All bind:this callbacks, in reverse order: children before parents. | |
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT | |
// for afterUpdates called during the initial onMount, which are called in | |
// reverse order: children before parents. | |
// Since callbacks might update component values, which could trigger another | |
// call to flush(), the following steps guard against this: | |
// 1. During beforeUpdate, any updated components will be added to the | |
// dirty_components array and will cause a reentrant call to flush(). Because | |
// the flush index is kept outside the function, the reentrant call will pick | |
// up where the earlier call left off and go through all dirty components. The | |
// current_component value is saved and restored so that the reentrant call will | |
// not interfere with the "parent" flush() call. | |
// 2. bind:this callbacks cannot trigger new flush() calls. | |
// 3. During afterUpdate, any updated components will NOT have their afterUpdate | |
// callback called a second time; the seen_callbacks set, outside the flush() | |
// function, guarantees this behavior. | |
const seen_callbacks = new Set(); | |
let flushidx = 0; // Do *not* move this inside the flush() function | |
/** @returns {void} */ | |
export function flush() { | |
// Do not reenter flush while dirty components are updated, as this can | |
// result in an infinite loop. Instead, let the inner flush handle it. | |
// Reentrancy is ok afterwards for bindings etc. | |
if (flushidx !== 0) { | |
return; | |
} | |
const saved_component = current_component; | |
do { | |
// first, call beforeUpdate functions | |
// and update components | |
try { | |
while (flushidx < dirty_components.length) { | |
const component = dirty_components[flushidx]; | |
flushidx++; | |
set_current_component(component); | |
update(component.$$); | |
} | |
} catch (e) { | |
// reset dirty state to not end up in a deadlocked state and then rethrow | |
dirty_components.length = 0; | |
flushidx = 0; | |
throw e; | |
} | |
set_current_component(null); | |
dirty_components.length = 0; | |
flushidx = 0; | |
while (binding_callbacks.length) binding_callbacks.pop()(); | |
// then, once components are updated, call | |
// afterUpdate functions. This may cause | |
// subsequent updates... | |
for (let i = 0; i < render_callbacks.length; i += 1) { | |
const callback = render_callbacks[i]; | |
if (!seen_callbacks.has(callback)) { | |
// ...so guard against infinite loops | |
seen_callbacks.add(callback); | |
callback(); | |
} | |
} | |
render_callbacks.length = 0; | |
} while (dirty_components.length); | |
while (flush_callbacks.length) { | |
flush_callbacks.pop()(); | |
} | |
update_scheduled = false; | |
seen_callbacks.clear(); | |
set_current_component(saved_component); | |
} | |
/** @returns {void} */ | |
function update($$) { | |
if ($$.fragment !== null) { | |
$$.update(); | |
run_all($$.before_update); | |
const dirty = $$.dirty; | |
$$.dirty = [-1]; | |
$$.fragment && $$.fragment.p($$.ctx, dirty); | |
$$.after_update.forEach(add_render_callback); | |
} | |
} | |
/** | |
* Useful for example to execute remaining `afterUpdate` callbacks before executing `destroy`. | |
* @param {Function[]} fns | |
* @returns {void} | |
*/ | |
export function flush_render_callbacks(fns) { | |
const filtered = []; | |
const targets = []; | |
render_callbacks.forEach((c) => (fns.indexOf(c) === -1 ? filtered.push(c) : targets.push(c))); | |
targets.forEach((c) => c()); | |
render_callbacks = filtered; | |
} | |