三种防重方案对比:标志位、防抖节流、缓存的适用场景

防止前端页面重复请求

防止前端页面的重复请求是提升用户体验和减轻服务器压力的重要策略之一。本文将介绍一些实用的方法来减少或防止重复请求。

1. ⚠️ 使用锁或标志位

设置一个锁(flag)或状态标志位来控制请求的发送。在首次点击或请求发起时设置该标志位,直到请求完成后再清除该标志,以阻止在请求未完成前再次发起相同的请求。

示例:

javascript 复制代码
let isRequestPending = false;

function fetchData() {
  if (isRequestPending) {
    return; // 如果请求已经在进行中,则不再发起新的请求
  }

  isRequestPending = true; // 设置请求标志
  fetch("your-api-endpoint")
    .then((response) => response.json())
    .then((data) => {
      console.log("请求成功:", data);
    })
    .catch((error) => {
      console.error("请求失败:", error);
    })
    .finally(() => {
      isRequestPending = false; // 请求结束,无论成功或失败,都清除请求标志
    });
}

// 模拟用户多次点击
fetchData();
fetchData(); // 这个请求将不会被执行

解释:

上述代码中,isRequestPending 变量充当一个锁。在 fetchData 函数执行前,会检查 isRequestPending 的状态。如果为 true,则表示上一个请求还在进行中,直接返回,避免重复发起请求。当请求开始时,isRequestPending 被设置为 true,请求完成后(无论成功或失败),在 finally 块中将其重置为 false,以便后续可以再次发起请求。这样,即使用户多次点击,也只会发起一次请求,直到该请求完成。

2. ✨ 使用防抖(Debounce)和节流(Throttle)

防抖和节流是限制函数执行频率的两种常见技术,它们可以有效防止重复请求。

  • 防抖(Debounce) :在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
  • 节流(Throttle):在规定的时间内只能触发一次函数。如果这个时间内触发多次函数,只有一次生效。

示例:

使用 lodash_.debounce_.throttle 方法:

javascript 复制代码
import _ from "lodash";

// 防抖函数
const fetchDataDebounced = _.debounce(fetchData, 300);

// 节流函数
const fetchDataThrottled = _.throttle(fetchData, 300);

// 使用防抖或节流方法来减少函数执行频率
button.addEventListener("click", fetchDataDebounced);

解释:

  • 防抖 :例如,当用户在搜索框中输入时,我们不希望每次按键都发送请求,而是希望用户停止输入一段时间(比如 300 毫秒)后再发送请求。_.debounce(fetchData, 300) 会创建一个防抖函数,只有当 fetchData 在 300 毫秒内没有再次被调用时,才会真正执行 fetchData。如果在 300 毫秒内再次调用,计时器会重新开始。
  • 节流 :例如,当用户频繁点击一个按钮时,我们可能只希望每隔一定时间(比如 300 毫秒)才触发一次点击事件。_.throttle(fetchData, 300) 会创建一个节流函数,在 300 毫秒内,无论 fetchData 被调用多少次,它都只会执行一次。

这两种技术在处理用户输入、窗口调整、滚动等高频事件时非常有用,可以有效减少不必要的请求,提升页面性能和用户体验。

3. 🔄 使用缓存结果(最佳办法)

对于一些数据不经常变化的请求,例如用户信息、配置数据等,可以将请求的结果缓存起来。下一次请求相同的资源时,先从缓存中读取数据,如果缓存有效,则无需再发起新的网络请求。

要达到这样的效果,可以设计一个请求缓存管理器,来管理并发的请求。如果有相同的请求(URL、参数、方法相同)时,只发起一次网络调用,然后将结果分发给所有等待的请求。这种模式通常可以通过一个简单的缓存对象来实现,该对象将请求的唯一标识作为键,对应的 Promise 作为值。

示例:

以下是一个基本实现的示例:

javascript 复制代码
class RequestCache {
  constructor() {
    this.cache = new Map();
  }

  // 生成请求的唯一标识符,这里仅以 URL 和 Method 为例,实际可能需要包括请求体等
  generateKey(url, method) {
    return `${method}:${url}`;
  }

  // 执行请求的方法,接受 fetch 的所有参数
  request(url, options = {}) {
    const { method = "GET" } = options;
    const key = this.generateKey(url, method);

    // 检查缓存中是否有相同的请求
    if (this.cache.has(key)) {
      return this.cache.get(key);
    }

    // 没有相同的请求,发起新的请求
    const requestPromise = fetch(url, options)
      .then((response) => response.json())
      .then((data) => {
        // 请求成功后,将其从缓存中移除
        this.cache.delete(key);
        return data;
      })
      .catch((error) => {
        // 请求失败也应该从缓存中移除
        this.cache.delete(key);
        throw error;
      });

    // 将新的请求 Promise 保存在缓存中
    this.cache.set(key, requestPromise);

    return requestPromise;
  }
}

// 使用示例
const cache = new RequestCache();
const URL = "https://api.example.com/data";

// 假设这三个请求几乎同时发起
cache.request(URL).then((data) => console.log("请求1:", data));
cache.request(URL).then((data) => console.log("请求2:", data));
cache.request(URL).then((data) => console.log("请求3:", data));

解释:

这个简单的 RequestCache 类通过一个内部的 Map 对象管理缓存的请求。当一个新的请求发起时,它会首先检查是否已经有相同的请求存在。如果已存在,那么它只返回先前请求的 Promise;如果不存在,它会发起一个新的网络请求,并将请求的 Promise 存储在缓存中,直到请求完成(无论是成功还是失败)之后,再将其从缓存中移除。

请注意,这里的示例非常基础,且主要用于说明如何缓存并复用请求的结果。在实际应用中,你可能还需要考虑更多细节,比如如何更精细地处理 POST 请求的请求体内容、如何设置缓存的过期时间、错误处理策略、缓存大小限制等。

总结

防止前端页面重复请求是优化用户体验和减轻服务器压力的关键。通过使用锁或标志位、防抖和节流技术,以及实现请求缓存,我们可以有效地管理和优化前端的网络请求。在实际开发中,应根据具体的业务场景和需求,选择最适合的策略或组合使用这些方法,以达到最佳的性能和用户体验。

希望这篇博客能帮助你更好地理解和应用这些前端知识!

相关推荐
Rocket MAN4 小时前
Spring Boot 缓存:工具选型、两级缓存策略、注解实现与进阶优化
spring boot·后端·缓存
weixin_46687 小时前
Redis数据库基础
数据库·redis·缓存
Lucky小小吴10 小时前
开源项目解读4-高性能并发缓存库Ristretto
缓存·开源·高并发·高性能缓存·实时数据处理·ristretto·tinylfu
亮子AI13 小时前
【NestJS】在 nest.js 项目中,如何使用 Postgresql 来做缓存?
开发语言·缓存·node.js·nest.js
好大的月亮14 小时前
oss中的文件替换后chrome依旧下载到缓存文件概述
前端·chrome·缓存
CodeAmaz16 小时前
SpringBoot两级缓存实现
spring boot·spring·缓存
小丁爱养花1 天前
Redis 内部编码/单线程模型/string
数据库·redis·缓存·1024程序员节
爬山算法1 天前
Redis(84)如何解决Redis的缓存击穿问题?
java·redis·缓存
Merlos_wind1 天前
【Redis典型应用——缓存详解】
数据库·redis·缓存