Skip to content
Xmo edited this page Oct 31, 2022 · 2 revisions
// 0. 引入once-init
import oi from "once-init";

// 1. 创建一个异步函数
async function foo() {
  // do something, for example, request backend data.
  const res = await axios.get("xxx.com");
  return res;
}

// 2. 用once-init封装这个异步函数
const oiFoo = oi(foo);

// 3. 执行封装后的函数
oiFoo.init();

通常来说,axios.get 请求很适合进行封装,你可以用refresh来代替原来的 axios.get 实现无感处理重复请求。

只需要下面这一行:

axios.get = oi(axios.get).refresh;

用例

不用 once-init

// 我们假设 axios.get("xxx.com") 返回的值是一个递增的数字,即第1次请求,会返回1,第2次请求会返回2,第n次请求会返回n。
await foo(); // 返回 1
await foo(); // 返回 2
await foo(); // 返回 3

使用 once-init

// once-init 会将重复执行重定向到第一次执行的结果上;(第一次执行后会缓存执行结果,类似单例模式)
await oiFoo.init(); // 返回 1
await oiFoo.init(); // 返回 1
await oiFoo.init(); // 返回 1

这意味着无论重复执行 oiFoo.init 多少次,foo 都只会执行第一次,返回第一次执行的结果;(就像缓存一样)

await Promise.all([oiFoo.init(), oiFoo.init(), oiFoo.init()]); // 返回 [1, 1, 1]
await Promise.all([oiFoo.init(), oiFoo.init(), oiFoo.init()]); // 返回 [1, 1, 1]
// 通常,如果你只会使用到init,你可以直接把 oiFoo 定义成 init 函数
const oiFoo = oi(foo).init;

await oiFoo();

如果你不使用缓存,只是希望防止同一时间发出重复请求,你可以使用refresh

// refresh和init在同一时间执行多次,都会阻止重复执行,多余的async function会返回第一次的结果;
await Promise.all([oiFoo.refresh(), oiFoo.refresh(), oiFoo.refresh()]); // 返回 [1, 1, 1]
// 但refresh如果当前没有其它重复的async function在执行,会刷新结果,并同时刷新缓存(影响到下一次init的返回);
await Promise.all([oiFoo.refresh(), oiFoo.refresh(), oiFoo.refresh()]); // 返回 [2, 2, 2]
await oiFoo.init(); // 返回 2

once-init 会区分参数,如果传入的异步函数有参,那么传入不同的参数将被视为两个不同的异步函数,不会共享缓存和执行队列;(使用lodash.isEqual判断参数是否相等)

下面这个复杂用例将会给你提供灵感:

// 假设 xxx.com/+ 会返回正数, xxx.com/- 会返回负数,两者有独立的缓存,且绝对值都递增
async function foo(op: "+" | "-") {
  const res = await axios.get(`xxx.com/${op}`);
  return res;
}

const oiFoo = oi(foo);
await oiFoo.init("-"); // 返回 -1
await oiFoo.refresh("-"); // 返回 -2
await oiFoo.refresh("-"); // 返回 -3

await oiFoo.refresh("+"); // 返回 1
await oiFoo.init("-"); // 返回 -3

Clone this wiki locally