svelte
Version:
Cybernetically enhanced web apps
72 lines (60 loc) • 1.85 kB
JavaScript
/** @import { Blocker, TemplateNode, Value } from '#client' */
import { flatten, increment_pending } from '../../reactivity/async.js';
import { get } from '../../runtime.js';
import {
hydrate_next,
hydrate_node,
hydrating,
set_hydrate_node,
set_hydrating,
skip_nodes
} from '../hydration.js';
/**
* @param {TemplateNode} node
* @param {Blocker[]} blockers
* @param {Array<() => Promise<any>>} expressions
* @param {(anchor: TemplateNode, ...deriveds: Value[]) => void} fn
*/
export function async(node, blockers = [], expressions = [], fn) {
var was_hydrating = hydrating;
var end = null;
if (was_hydrating) {
hydrate_next();
end = skip_nodes(false);
}
if (expressions.length === 0 && blockers.every((b) => b.settled)) {
fn(node);
// This is necessary because it is not guaranteed that the render function will
// advance the hydration node to $.async's end marker: it may stop at an inner
// block's end marker (in case of an inner if block for example), but it also may
// stop at the correct $.async end marker (in case of component child) - hence
// we can't just use hydrate_next()
// TODO this feels indicative of a bug elsewhere; ideally we wouldn't need
// to double-traverse in the already-resolved case
if (was_hydrating) {
set_hydrate_node(end);
}
return;
}
const decrement_pending = increment_pending();
if (was_hydrating) {
var previous_hydrate_node = hydrate_node;
set_hydrate_node(end);
}
flatten(blockers, [], expressions, (values) => {
if (was_hydrating) {
set_hydrating(true);
set_hydrate_node(previous_hydrate_node);
}
try {
// get values eagerly to avoid creating blocks if they reject
for (const d of values) get(d);
fn(node, ...values);
} finally {
if (was_hydrating) {
set_hydrating(false);
}
decrement_pending();
}
});
}