50 lines
2.8 KiB
TypeScript
50 lines
2.8 KiB
TypeScript
/**
|
|
* 共享合作调度软预算,供导入、复制、恢复编码等逐项计算调用。
|
|
* 既按耗时判断,也按工作量兜底,避免粗粒度/倒退时钟让长任务一直不让出;单个不可分步骤仍由调用方限大小。
|
|
*/
|
|
import {fault} from '../canvas-core/values.ts';
|
|
|
|
/** 注入时钟、让出任务与目标时间片;它们不改变记录语义或操作身份。 */
|
|
export type ImportScheduling = {
|
|
now?: () => number;
|
|
yieldTask?: () => Promise<void>;
|
|
frameBudgetMs?: number;
|
|
};
|
|
|
|
/** A soft time slice with a work threshold, even when the clock is coarse.
|
|
* The caller bounds each indivisible parse/decode operation separately. */
|
|
/** 默认 6ms 软目标,闭包保存本时间片 started/work/unchecked 三个计数,不存任务或文档。
|
|
* step 可能同步返回 undefined;只有返回 Promise 且调用方 await 时才形成有效背压,应串行使用同一预算。
|
|
* 自定义 yieldTask 决定让出到微任务还是后续任务;预算不保证浏览器一定在该点完成一帧绘制。
|
|
* 构造只校验 frameBudgetMs,不预先检查 signal;units 应由调用者提供非负有限工作量。 */
|
|
export function createImportBudget(signal: AbortSignal, options: ImportScheduling = {}) {
|
|
const budget = options.frameBudgetMs ?? 6;
|
|
if (!Number.isFinite(budget) || budget <= 0) throw fault('invalid_import_budget');
|
|
const now = options.now ?? (() => performance.now());
|
|
const yieldTask = options.yieldTask ?? (() => new Promise<void>(resolve => setTimeout(resolve, 0)));
|
|
// 每128工作单位才读一次时钟,减少高频开销;总量16384强制让出,不能因时间测不准而无限执行。
|
|
let started = now(), work = 0, unchecked = 0;
|
|
return {
|
|
/** 累加 units;未满 128 个未检查单位且总量未满 16384 时直接返回,连 signal 也尚未检查。
|
|
* 阈值到达才读时钟;未超时保留 work 累计,超时/时间倒退或非有限值/工作量封顶则 await yieldTask。
|
|
* yield 后复查取消,成功才重设 started/work;读取时钟或 yield 抛错直接传播,不隐式重试。
|
|
* units 无运行时校验,负数/NaN 会破坏计数假设;本函数不测量单次实际 CPU 成本或字节数。 */
|
|
step(units = 1): Promise<void> | undefined {
|
|
work += units;
|
|
unchecked += units;
|
|
if (unchecked < 128 && work < 16384) return;
|
|
unchecked = 0;
|
|
if (signal.aborted) throw fault('import_aborted');
|
|
const elapsed = now() - started;
|
|
if (work < 16384 && elapsed >= 0 && elapsed < budget) return;
|
|
// 这里的异步边界才实际交出执行权;预算是 cooperative,不能抢占前面已经运行的同步工作。
|
|
return (async () => {
|
|
await yieldTask();
|
|
if (signal.aborted) throw fault('import_aborted');
|
|
started = now();
|
|
work = 0;
|
|
})();
|
|
},
|
|
};
|
|
}
|