Skip to content

once / singleFlight

只执行一次并复用结果 —— 用于惰性初始化:配置解析、昂贵的一次性计算、懒 getter。 once 是同步版,singleFlight 是异步版。

API

once(fn)

参数说明类型默认值
fn只执行一次的函数;传非函数值则原样返回该值Function | any必填

返回包装后的函数。首次调用求值并缓存,之后无论传什么参数都直接返回首次的结果。

singleFlight(fn)

参数说明类型默认值
fn只执行一次的异步函数() => Promise<T>必填

返回包装后的函数,并附带:

成员说明类型
reset()丢弃缓存结果,下次调用重新执行 fn() => void
started是否已有结果或正在执行中boolean

memoize(fn)

once 的废弃别名,见下方说明。

示例

js
import { once, singleFlight } from 'ranuts';

// 同步:首次访问时才解析配置
const config = once(() => JSON.parse(rawConfig));
config(); // 解析
config(); // 走缓存

// 异步:无论多少调用方并发,数据库只打开一次
const ready = singleFlight(() => db.openDataBase());
await Promise.all([ready(), ready(), ready()]); // 只打开一次

注意

  1. once 不按参数缓存。只有首次调用的参数生效。需要按参数缓存请自行用 Map
  2. once 求值后会释放 fn,让它闭包捕获的资源可被回收。
  3. singleFlight 不缓存失败。失败会清空缓存,让一次偶发的网络抖动仍可重试 —— 缓存一个 rejected promise 会把偶发故障变成永久故障。
  4. 并发调用共享同一个在途 promise,N 个调用方竞争时 fn 也只跑一次。这正是 「init() 返回 void → 调用方无从 await → 早期写操作全部失败」这个经典 bug 的解法。

0.3 改名

memoize 是个有误导的名字 —— 它从不按参数缓存,只是「执行一次」。现已成为 once 的别名并标记废弃。 它此前的类型签名也是错的(声明为无参却透传参数),现在从 fn 推导。

Released under the MIT License.