实现一个以 Promise 状态判断的 throttle

起因

最近碰到一个面试题

页面里面有一个按钮,点击之后需要向服务端发起一个请求,在这个请求没有返回之前,无论点击多少次都不会发送第二遍请求。

基础写法

typescript 复制代码
const fetchData: (
  duration: number,
  status: "success" | "error"
) => Promise<"success" | "error"> = (duration, status = "success") => {
  console.log('Enter FetchData Function');
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      status === "success" ? resolve(status) : reject(status);
    }, duration);
  });
};


let lock = false;
const baseButtonDom = document.getElementById("base") as HTMLButtonElement;
const onClickFunc = async () => {
  if (!lock) {
    lock = true;
    const data = await fetchData(3000, "success");
    console.log(data);
    lock = false;
  }
};
baseButtonDom.addEventListener("click", onClickFunc);

如果只针对这个需求的话,是可以满足的,但是缺点也很明显,这个写法不够通用。

采用 Lodash 的防抖节流

先看看 他们的异同点

相同点:

  • 都可以通过使用 setTimeout 实现
  • 目的都是,降低回调执行频率。节省计算资源

不同点:

实现 执行时机
防抖 在一段连续操作结束后,处理回调,利用clearTimeout和 setTimeout实现 一定时间连续触发的事件,只在最后执行一次
节流 在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能 一段时间内只执行一次
javascript 复制代码
import { debounce, throttle } from "lodash";

const clickCallback = async () => {
  const data = await fetchData(3000, "success");
  console.log(data);
};
const debounceButtonDom = document.getElementById("debounce") as HTMLButtonElement;
const throttleButtonDom = document.getElementById("throttle") as HTMLButtonElement;
throttleButtonDom.addEventListener(
  "click",
  throttle(clickCallback, 3000, {
    leading: false, // 指定调用在节流开始前
  })
);
debounceButtonDom.addEventListener("click", debounce(clickCallback, 3000));

在快速点击三下的场景下,防抖和节流的打印表现都是一致的

在当连续点击的场景下会有所不同

  • 防抖 : 由于 防抖 会一直重置事件,所以会一直卡着不会进行输出,直到放开鼠标才会执行事件。
  • 节流 : 节流的实现逻辑是在指定时间内只会实现一次,但在连续点击时,只要超过设置的时间,就会进行下一次调用,没办法满足需求 " 在请求没有返回之前,无论点击多少次都不会发送第二遍请求。 "

所以 防抖和节流 达不到预期想要的结果;

最终实现

采用节流的逻辑实现,修改他的判断条件即可。节流是在规定时间内只执行一次,那么把这个条件修改成 Promise 状态判断就可以满足。pending 状态阻止调用,fulfilled 状态可以继续调用。那么函数 重新执行 的时机就是 fulfilled 的时候。也就满足了需求中的 " 在请求没有返回之前,无论点击多少次都不会发送第二遍请求。 "

typescript 复制代码
// 获取 Promise Status
const promiseState = (promise: Promise<unknown>) => {
  const target = {};
  return Promise.race([promise, target]).then(
    (value) => (value === target ? "pending" : "fulfilled"),
    () => "rejected"
  );
};
// 判读是否是 Promise
const isPromise = (promise: Promise<unknown>) => {
  return promise && promise.then && typeof promise.then === "function";
}

/**
 * 
 * @param {Asyncfunction} fn - 传入一个异步方法 
 * @returns {function}
 */
export const throttleForPromise = (fn: Function) => {
  let status: Promise<unknown> | null;
  return function (this: any) {
    if (status && isPromise(status)) {
      promiseState(status).then((resultStatus) => {
        if (resultStatus === "fulfilled") status = fn.apply(this, arguments);
      });
    } else {
      status = fn.apply(this, arguments);
    }
  };
};
  • 为什么不用 instanceof 判断 Promise ?
    instanceof 只能判断符合 ES 标准的 Promise ,不考虑兼容 Promise A+ 规范的的情况下完全可以
  • 还有其他办法拿到 Promise 的状态吗 ?
    Promise 的状态是一个内部状态,暂时没有办法直接拿到,如果有其他更好的获取方式的话,望告知
typescript 复制代码
const clickCallback = async () => {
  try {
    const data = await fetchData(3000, "success");
    console.log(data);
  } catch (error) {
    console.log(error);
  }
};
const promiseButtonDom = document.getElementById("throttle-promise") as HTMLButtonElement;
promiseButtonDom.addEventListener("click", throttleForPromise(clickCallback));

连续点击三下

连续不停点击 且 状态为 reject 时

均可以满足在 Promise 状态为 pending 时阻止继续调用,状态为 fulfilled 时重新执行。

以上 ,就是 throttlePromise 函数的简单实现了。函数只针对该需求的一个扩展,如果有更好的写法,欢迎讨论。

参考

在JavaScript获取Promise的状态_获取promise状态-CSDN博客

相关推荐
object not found14 分钟前
WooCommerce 接入 KPay 后订单完成页不显示订单信息:returnUrl 动态参数问题解决
前端·wordpress·独立站支付
yume_sibai25 分钟前
04-Nginx部署实战完全指南(前端部署 + 后端代理 + 监控告警 + 故障排查)
运维·前端·nginx
data analyse 45635 分钟前
数据合规的同意记录怎么留存?
前端·人工智能·数据分析
linux_cfan41 分钟前
videojs v10 源代码系列解读:35 · HLS 解析器:multivariant 与 media playlist
前端·javascript·音视频
IT_陈寒42 分钟前
Redis键过期失效?这个坑我踩得明明白白
前端·人工智能·后端
web打印社区44 分钟前
汽修 / 4S 店:维修工单、结算单网页怎么静默出纸
开发语言·前端·javascript·chrome·ecmascript
liangshanbo12151 小时前
Webpack的分包策略面试题
前端·webpack·node.js
ttwuai1 小时前
Golang Web后台管理框架推荐:Gin、GoFrame与数据面板项目怎么分
前端·golang·gin
广州华水科技1 小时前
北斗GNSS变形监测系统在水库安全监测中的应用与优势
前端