this repo has no description
at main 135 lines 4.3 kB view raw
1import { run_all } from './utils.js'; 2import { current_component, set_current_component } from './lifecycle.js'; 3 4export const dirty_components = []; 5export const intros = { enabled: false }; 6export const binding_callbacks = []; 7 8let render_callbacks = []; 9 10const flush_callbacks = []; 11 12const resolved_promise = /* @__PURE__ */ Promise.resolve(); 13 14let update_scheduled = false; 15 16/** @returns {void} */ 17export function schedule_update() { 18 if (!update_scheduled) { 19 update_scheduled = true; 20 resolved_promise.then(flush); 21 } 22} 23 24/** @returns {Promise<void>} */ 25export function tick() { 26 schedule_update(); 27 return resolved_promise; 28} 29 30/** @returns {void} */ 31export function add_render_callback(fn) { 32 render_callbacks.push(fn); 33} 34 35/** @returns {void} */ 36export function add_flush_callback(fn) { 37 flush_callbacks.push(fn); 38} 39 40// flush() calls callbacks in this order: 41// 1. All beforeUpdate callbacks, in order: parents before children 42// 2. All bind:this callbacks, in reverse order: children before parents. 43// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT 44// for afterUpdates called during the initial onMount, which are called in 45// reverse order: children before parents. 46// Since callbacks might update component values, which could trigger another 47// call to flush(), the following steps guard against this: 48// 1. During beforeUpdate, any updated components will be added to the 49// dirty_components array and will cause a reentrant call to flush(). Because 50// the flush index is kept outside the function, the reentrant call will pick 51// up where the earlier call left off and go through all dirty components. The 52// current_component value is saved and restored so that the reentrant call will 53// not interfere with the "parent" flush() call. 54// 2. bind:this callbacks cannot trigger new flush() calls. 55// 3. During afterUpdate, any updated components will NOT have their afterUpdate 56// callback called a second time; the seen_callbacks set, outside the flush() 57// function, guarantees this behavior. 58const seen_callbacks = new Set(); 59 60let flushidx = 0; // Do *not* move this inside the flush() function 61 62/** @returns {void} */ 63export function flush() { 64 // Do not reenter flush while dirty components are updated, as this can 65 // result in an infinite loop. Instead, let the inner flush handle it. 66 // Reentrancy is ok afterwards for bindings etc. 67 if (flushidx !== 0) { 68 return; 69 } 70 const saved_component = current_component; 71 do { 72 // first, call beforeUpdate functions 73 // and update components 74 try { 75 while (flushidx < dirty_components.length) { 76 const component = dirty_components[flushidx]; 77 flushidx++; 78 set_current_component(component); 79 update(component.$$); 80 } 81 } catch (e) { 82 // reset dirty state to not end up in a deadlocked state and then rethrow 83 dirty_components.length = 0; 84 flushidx = 0; 85 throw e; 86 } 87 set_current_component(null); 88 dirty_components.length = 0; 89 flushidx = 0; 90 while (binding_callbacks.length) binding_callbacks.pop()(); 91 // then, once components are updated, call 92 // afterUpdate functions. This may cause 93 // subsequent updates... 94 for (let i = 0; i < render_callbacks.length; i += 1) { 95 const callback = render_callbacks[i]; 96 if (!seen_callbacks.has(callback)) { 97 // ...so guard against infinite loops 98 seen_callbacks.add(callback); 99 callback(); 100 } 101 } 102 render_callbacks.length = 0; 103 } while (dirty_components.length); 104 while (flush_callbacks.length) { 105 flush_callbacks.pop()(); 106 } 107 update_scheduled = false; 108 seen_callbacks.clear(); 109 set_current_component(saved_component); 110} 111 112/** @returns {void} */ 113function update($$) { 114 if ($$.fragment !== null) { 115 $$.update(); 116 run_all($$.before_update); 117 const dirty = $$.dirty; 118 $$.dirty = [-1]; 119 $$.fragment && $$.fragment.p($$.ctx, dirty); 120 $$.after_update.forEach(add_render_callback); 121 } 122} 123 124/** 125 * Useful for example to execute remaining `afterUpdate` callbacks before executing `destroy`. 126 * @param {Function[]} fns 127 * @returns {void} 128 */ 129export function flush_render_callbacks(fns) { 130 const filtered = []; 131 const targets = []; 132 render_callbacks.forEach((c) => (fns.indexOf(c) === -1 ? filtered.push(c) : targets.push(c))); 133 targets.forEach((c) => c()); 134 render_callbacks = filtered; 135}