高级前端
js
【Q743】实现 batchFn 函数,可以批量执行函数

实现 batchFn 函数,可以批量执行函数

更多描述 补全及实现一下函数

let executeCount = 0;
const targetFn = async (nums) => {
  executeCount++;
  return nums.map((num) => 2 * num + 1);
};
 
const batcher = (fn) => {
  // todo batch logic
  return () => {};
};
 
const batchedFn = batcher(targetFn);
 
const main = async () => {
  const [result1, result2, result3] = await Promise.all([
    batchedFn([1, 2, 3]),
    batchedFn([4, 5]),
    batchedFn([6, 7]),
  ]);
 
  console.log(result1, result2, result3);
  console.log(executeCount); // 预期为 1
};
 
main();

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 800 (opens in a new tab)

Author 回答者: shfshanyue (opens in a new tab)

graphql/loader (opens in a new tab) 以及 trpc (opens in a new tab) 中均有关于 batch 的实现,意在提升性能,将多次 IO 合并为一次 IO

其关键在于一次事件循环中的微任务队列存储所有的 batchKeys。

代码片段及执行结果见码上掘金 (opens in a new tab)

const batcher = (fn) => {
  // todo batch logic
  let allArgs = [];
  // 能够实现 batch 的关键所在
  // 此处 fn(allArgs) 甚至可以实现为 fn([...new Set(allArgs)]),性能更好一些
  const wait = Promise.resolve().then(() => fn(allArgs));
 
  return async (args) => {
    allArgs = [...allArgs, ...args];
    const result = await wait;
 
    // allArgs 与 result 形成的一个 Map
    // 借助于 lodash 可以更可读化地写成 Object.fromEntries(_.zip(allArgs, result))
    const resultMap = result.reduce((acc, x, i) => {
      const v = allArgs[i];
      acc[v] = x;
      return acc;
    }, {});
    return args.map((a) => resultMap[a]);
  };
};

Author 回答者: MJWade96 (opens in a new tab)

运行结果好像是输出 3 个 [3, 5, 7],返回的 promiseargs 应该都指向了第一次调用时传入的参数

Author 回答者: MJWade96 (opens in a new tab)

网上看到了另一种解法:SegmentFault (opens in a new tab)

Author 回答者: shfshanyue (opens in a new tab)

@MJWade96 已修复,并配上相应的注释、码上掘金的 Playground 以及相关源码参考。