名称: 同步构造异步属性UI渲染门模式 描述: 同步构造与异步属性模式。用于创建需要异步初始化但必须可模块导出并在UI组件中同步使用的客户端。 元数据: 作者: epicenter 版本: ‘1.0’
同步构造,异步属性
客户端的初始化是同步的。异步工作存储为一个属性,您可以await,同时传递引用。
何时应用此模式
在以下情况使用:
- 需要异步客户端初始化(IndexedDB、服务器连接、文件系统)
- 模块导出需要在没有
await的情况下可导入 - UI组件希望同步访问客户端
- SvelteKit应用,您希望在准备就绪时门控渲染
信号表明您正在处理异步构造问题:
- 到处使用
await getX()模式 - 捆绑器抱怨顶级await
- 包装单例访问的getter函数
- 组件无法直接导入客户端
问题
异步构造函数无法导出:
// 这不可行
export const client = await createClient(); // 顶级await破坏捆绑器
因此您最终会使用getter模式:
let client: Client | null = null;
export async function getClient() {
if (!client) {
client = await createClient();
}
return client;
}
// 每个使用者必须await
const client = await getClient();
每个调用点都需要await。您传递的是承诺而不是对象。
模式
使构造同步。将异步工作附加到对象:
// client.ts
export const client = createClient();
// 同步访问立即工作
client.save(data);
client.load(id);
// 当需要时await异步工作
await client.whenSynced;
构造立即返回。异步初始化(从磁盘加载、连接到服务器)在后台发生,并通过whenSynced跟踪。
UI渲染门
在Svelte中,在根组件处await一次:
<!-- +layout.svelte -->
<script>
import { client } from '$lib/client';
</script>
{#await client.whenSynced}
<LoadingSpinner />
{:then}
{@render children?.()}
{/await}
门控保证:在任何子组件的脚本运行之前,异步工作已完成。子组件使用同步访问而无需检查准备状态。
实现
withCapabilities()流畅构建器将异步工作附加到同步构造的对象:
function createClient() {
const state = initializeSyncState();
return {
save(data) {
/* 同步方法 */
},
load(id) {
/* 同步方法 */
},
withCapabilities({ persistence }) {
const whenSynced = persistence(state);
return Object.assign(this, { whenSynced });
},
};
}
// 用法
export const client = createClient().withCapabilities({
persistence: (state) => loadFromIndexedDB(state),
});
之前与之后
| 方面 | 异步构造 | 同步 + whenSynced |
|---|---|---|
| 模块导出 | 无法直接导出 | 导出对象 |
| 消费者代码 | 到处await getX() |
直接导入,同步使用 |
| UI集成 | 尴尬的承诺处理 | 单一{#await}门控 |
| 类型签名 | Promise<X> |
X带.whenSynced |
真实世界示例: y-indexeddb
Yjs生态系统普遍使用此模式:
const provider = new IndexeddbPersistence('my-db', doc);
// 构造函数立即返回
provider.on('update', handleUpdate); // 同步访问工作
await provider.whenSynced; // 当需要时等待
他们从不阻塞构造。异步工作总是延迟到您可以await的属性。