once / singleFlight
只执行一次并复用结果,用于惰性初始化:配置解析、昂贵的一次性计算、懒 getter。
once 是同步版,singleFlight 是异步版。
API
once(fn)
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
fn |
只执行一次的函数;传非函数值则原样返回该值 | Function | any |
必填 |
返回包装后的函数。首次调用求值并缓存,之后无论传什么参数都直接返回首次的结果。
singleFlight(fn)
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
fn |
只执行一次的异步函数 | () => Promise<T> |
必填 |
返回包装后的函数,并附带:
| 成员 | 说明 | 类型 |
|---|---|---|
reset() |
丢弃缓存结果,下次调用重新执行 fn |
() => void |
started |
是否已有结果或正在执行中 | boolean |
memoize(fn)
once 的废弃别名,见下方说明。
示例
import { once, singleFlight } from 'ranuts';
// 同步:首次访问时才解析配置
const config = once(() => JSON.parse(rawConfig));
config(); // 解析
config(); // 走缓存
// 异步:无论多少调用方并发,数据库只打开一次
const ready = singleFlight(() => db.openDataBase());
await Promise.all([ready(), ready(), ready()]); // 只打开一次注意
once不按参数缓存。只有首次调用的参数生效。需要按参数缓存请自行用Map。once求值后会释放fn,让它闭包捕获的资源可被回收。singleFlight不缓存失败。失败会清空缓存,让一次偶发的网络抖动仍可重试:缓存一个 rejected promise 会把偶发故障变成永久故障。- 并发调用共享同一个在途 promise,N 个调用方竞争时
fn也只跑一次。这正是「init()返回 void → 调用方无从 await → 早期写操作全部失败」这个经典 bug 的解法。