【前端并发请求控制:必要性与实现策略】

news2025/6/6 20:28:43

前端并发请求控制:必要性与实现策略

一、引言

在现代 Web 开发中,处理大量异步请求是一个常见场景。虽然浏览器和服务器都有其并发限制机制,但前端实现并发控制仍然具有其独特的价值和必要性。本文将深入探讨这个话题。

二、现有的并发限制机制

2.1 浏览器限制

浏览器对并发请求有默认的限制:

// 主流浏览器的并发限制(同一域名)
const browserLimits = {
    Chrome: 6,
    Firefox: '6-8',
    Safari: 6,
    Edge: 6
};

// 但这个限制是按域名的
const requests = [
    'https://api1.example.com/data',  // 域名1:可以6个并发
    'https://api2.example.com/data',  // 域名2:可以6个并发
    'https://api3.example.com/data'   // 域名3:可以6个并发
];

2.2 服务器限制

服务器通常通过限流来控制并发:

// 典型的服务器限流响应
{
    status: 429,
    message: 'Too Many Requests',
    retry_after: 30
}

三、为什么需要前端并发控制

3.1 资源优化

// 不控制并发的问题
urls.forEach(async url => {
    try {
        await fetch(url);
    } catch (e) {
        if (e.status === 429) {
            // 1. 请求已经发出,消耗了资源
            // 2. 服务器已经处理了请求
            // 3. 网络带宽已经使用
            retry(url);
        }
    }
});

// 控制并发的优势
await fetchWithLimit(urls, 5);
// 1. 预防性控制,避免资源浪费
// 2. 减少服务器压力
// 3. 优化网络资源使用

3.2 更好的用户体验

// 带进度反馈的并发控制
async function fetchWithProgress(urls, limit = 5) {
    let completed = 0;
    const total = urls.length;
    
    return fetchWithLimit(urls, limit, {
        onProgress: () => {
            completed++;
            updateUI(`进度:${completed}/${total}`);
        },
        onError: (error, url) => {
            showError(`请求失败:${url}`);
        }
    });
}

四、实现并发控制

4.1 基础实现

async function fetchWithLimit(urls, limit = 5) {
    const results = [];
    const executing = new Set();
    
    for (const url of urls) {
        const promise = fetch(url).then(response => {
            executing.delete(promise);
            return response;
        });
        
        executing.add(promise);
        results.push(promise);
        
        if (executing.size >= limit) {
            await Promise.race(executing);
        }
    }
    
    return Promise.all(results);
}

4.2 增强版实现

class RequestController {
    constructor(options = {}) {
        this.limit = options.limit || 5;
        this.timeout = options.timeout || 5000;
        this.retries = options.retries || 3;
        this.queue = [];
        this.executing = new Set();
    }

    async addRequest(url, options = {}) {
        const request = {
            url,
            priority: options.priority || 0,
            retryCount: 0
        };

        this.queue.push(request);
        this.processQueue();
    }

    async processQueue() {
        if (this.executing.size >= this.limit) {
            return;
        }

        // 按优先级排序
        this.queue.sort((a, b) => b.priority - a.priority);

        while (this.queue.length && this.executing.size < this.limit) {
            const request = this.queue.shift();
            this.executeRequest(request);
        }
    }

    async executeRequest(request) {
        const promise = this.fetchWithTimeout(request)
            .catch(error => this.handleError(request, error));

        this.executing.add(promise);
        
        promise.finally(() => {
            this.executing.delete(promise);
            this.processQueue();
        });

        return promise;
    }

    async fetchWithTimeout(request) {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.timeout);

        try {
            const response = await fetch(request.url, {
                signal: controller.signal
            });
            clearTimeout(timeoutId);
            return response;
        } catch (error) {
            clearTimeout(timeoutId);
            throw error;
        }
    }

    async handleError(request, error) {
        if (request.retryCount < this.retries) {
            request.retryCount++;
            this.queue.unshift(request);
            await new Promise(resolve => 
                setTimeout(resolve, Math.pow(2, request.retryCount) * 1000)
            );
        } else {
            throw error;
        }
    }
}

五、实际应用场景

5.1 文件上传

class FileUploader {
    constructor(options = {}) {
        this.controller = new RequestController(options);
        this.chunkSize = options.chunkSize || 1024 * 1024;
    }

    async uploadFile(file) {
        const chunks = this.splitFile(file);
        const uploads = chunks.map((chunk, index) => ({
            url: '/upload',
            body: chunk,
            priority: chunks.length - index // 优先上传文件的前面部分
        }));

        return this.controller.addRequests(uploads);
    }
}

5.2 数据批量处理

class DataProcessor {
    constructor(options = {}) {
        this.controller = new RequestController(options);
    }

    async processDataset(items) {
        const batches = this.createBatches(items, 100);
        return this.controller.addRequests(batches.map(batch => ({
            url: '/process',
            body: batch,
            priority: batch.some(item => item.isUrgent) ? 1 : 0
        })));
    }
}

六、最佳实践建议

动态调整并发数:

function getOptimalConcurrency() {
    const connection = navigator.connection;
    if (!connection) return 5;

    switch (connection.effectiveType) {
        case '4g': return 6;
        case '3g': return 4;
        case '2g': return 2;
        default: return 3;
    }
}

实现优先级控制:

const priorities = {
    HIGH: 3,
    MEDIUM: 2,
    LOW: 1
};

requests.sort((a, b) => b.priority - a.priority);

错误处理和重试策略:

async function retryWithBackoff(fn, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await fn();
        } catch (error) {
            if (i === maxRetries - 1) throw error;
            await delay(Math.pow(2, i) * 1000);
        }
    }
}

七、总结

前端并发控制虽然看似多余,但实际上是提升应用性能和用户体验的重要手段:

优势:

  • 预防性能问题
  • 提供更好的用户体验
  • 更灵活的控制策略
  • 更优雅的错误处理

实现考虑:

  • 动态并发数调整
  • 请求优先级
  • 超时处理
  • 错误重试
  • 进度反馈

应用场景:

  • 文件上传下载
  • 数据批量处理
  • API 批量调用
  • 资源预加载

通过合理的并发控制,我们可以在保证应用性能的同时,提供更好的用户体验。这不仅仅是一个技术问题,更是一个用户体验和资源优化的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2399403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从OSI到TCP/IP:网络协议的演变与作用

个人主页&#xff1a;chian-ocean 文章专栏-NET 从OSI到TCP/IP&#xff1a;网络协议的演变与作用 个人主页&#xff1a;chian-ocean文章专栏-NET 前言网络发展LANWAN 协议举个例子&#xff1a; 协议的产生背景 协议的标准化OSI模型参考OSI各个分层的作用各层次的功能简介 TCP/…

Stream流性能分析及优雅使用

文章目录 摘要一、Stream原理解析1.1、Stream总概1.2、Stream运行机制1.2.1、创建结点1.2.1、搭建流水线1.2.3、启动流水线 1.3、ParallelStream 二、性能对比三、优雅使用3.1 Collectors.toMap()3.2 findFirst()&#xff0c;findAny()3.3 增删元素3.4 ParallelStream 四、总结…

【和春笋一起学C++】(十七)C++函数新特性——内联函数和引用变量

C提供了新的函数特性&#xff0c;使之有别于C语言。主要包括&#xff1a; 内联函数&#xff1b;按引用传递变量&#xff1b;默认参数值&#xff1b;函数重载&#xff08;多态&#xff09;&#xff1b;模版函数&#xff1b; 因篇幅限制&#xff0c;本文首先介绍内联函数和引用…

proteus新建工程

1 点击新建工程 2 输入项目名&#xff0c;选择工程文件夹 3 下一步 4 不创建pcb 5 直接下一步 6 点击完成 7 创建完毕

RTC实时时钟DS1338Z-33/PT7C433833WEX国产替代FRTC1338S

FRTC1338S是NYFEA徕飞公司推出的一种高性能的实时时钟芯片&#xff0c;它采用了SOP8封装技术&#xff0c;这种技术因其紧凑的尺寸和出色的性能而被广泛应用于各类电子设备中。 FRTC1338S串行实时时钟(RTC)是一种低功耗的全二进制编码十进制(BCD)时钟/日历外加56字节的非易失性…

Redis命令使用

Redis是以键值对进行数据存储的&#xff0c;添加数据和查找数据最常用的2个指令就是set和get。 set&#xff1a;set指令用来添加数据。把key和value存储进去。get&#xff1a;get指令用来查找相应的键所对应的值。根据key来取value。 首先&#xff0c;我们先进入到redis客户端…

【免费数据】1980-2022年中国2384个站点的水质数据

水&#xff0c;是生命之源&#xff0c;关乎着地球上每一个生物的生存与发展。健康的水生生态系统维持着整个水生态的平衡与活力&#xff1b;更是确保人类能持续获得清洁水源的重要保障。水质数据在水质研究、海洋生物量测算以及生物多样性评估等诸多关键领域都扮演着举足轻重的…

Git 极简使用指南

Git 是一个强大的分布式版本控制系统&#xff0c;但入门只需要掌握几个核心概念和命令。本指南旨在帮助你快速上手&#xff0c;处理日常开发中最常见的 80% 的场景。 核心概念 仓库 (Repository / Repo): 你的项目文件夹&#xff0c;包含了项目的所有文件和完整的历史记录。…

力扣刷题Day 69:搜索二维矩阵(74)

1.题目描述 2.思路 首先判断target是否有可能在矩阵的某一行里&#xff0c;没可能直接返回False&#xff0c;有可能就在这一行里二分查找。 3.代码&#xff08;Python3&#xff09; class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> boo…

MySQL指令个人笔记

MySQL学习&#xff0c;SQL语言笔记 一、MySQL 1.1 启动、停止 启动 net start mysql83停止 net stop mysql831.2 连接、断开 连接 mysql -h localhost -P 3306 -u root -p断开 exit或者ctrlc 二、DDL 2.1 库管理 2.1.1 直接创建库 使用默认字符集和排序方式&#xf…

2022年 国内税务年鉴PDF电子版Excel

2022年 国内税务年鉴PDF电子版Excelhttps://download.csdn.net/download/2401_84585615/89784658 https://download.csdn.net/download/2401_84585615/89784658 2022年国内税务年鉴是对中国税收政策、税制改革和税务管理实践的全面总结。这份年鉴详细记录了中国税收系统的整体状…

基于Java的OPCDA采集中间件

1.软件功能及技术特点简介&#xff1a; 软件功能及技术特点简介&#xff1a; OPCDA是基于Java语言开发的OPC client&#xff08;OPC客户端&#xff09;跨平台中间件软件&#xff0c;他支持OPC SERVER的OPC DA1.0/2.0/3.0。OPCDA实时采集数据&#xff08;包括实时数据、报警数…

vue2 项目中 npm run dev 运行98% after emitting CopyPlugin 卡死

今天在运行项目时&#xff0c;发现如下问题&#xff1a; 开始以为是node_modules依赖的问题&#xff0c;于是重新 npm install&#xff0c;重启项目后还是未解决。 在网上找了一圈发现有人说是 require引入图片地址没有写。在我的项目中排查没有这个问题&#xff0c;最后发现某…

JavaScript 性能优化实战:从原理到框架的全栈优化指南

在 Web 应用复杂度指数级增长的今天&#xff0c;JavaScript 性能优化已成为衡量前端工程质量的核心指标。本文将结合现代浏览器引擎特性与一线大厂实践经验&#xff0c;构建从基础原理到框架定制的完整优化体系&#xff0c;助你打造高性能 Web 应用。 一、性能优化基础&#x…

2025年- H61-Lc169--74.搜索二维矩阵(二分查找)--Java版

1.题目描述 2.思路 方法一&#xff1a; 定义其实坐标&#xff0c;右上角的元素&#xff08;0&#xff0c;n-1&#xff09;。进入while循环&#xff08;注意边界条件&#xff0c;行数小于m&#xff0c;列数要&#xff1e;0&#xff09;从右上角开始开始向左遍历&#xff08;比当…

【黄金评论】美元走强压制金价:基于NLP政策因子与ARIMA-GARCH的联动效应解析

一、基本面&#xff1a;多因子模型解析黄金承压逻辑 1. 政策冲击因子驱动美元强势 通过NLP模型对关税政策文本进行情感分析&#xff0c;构建政策不确定性指数&#xff08;PUI&#xff09;达89.3&#xff0c;触发美元避险需求溢价。DSGE模型模拟显示&#xff0c;钢铁关税上调至…

Flink进阶之路:解锁大数据处理新境界

目录 一、Flink 基础回顾 二、Flink 进阶知识深入 2.1 数据类型与序列化 2.2 双流 Join 操作 2.3 复杂事件处理&#xff08;CEP&#xff09; 2.4 状态管理与优化 三、Flink 在实际场景中的应用 3.1 实时智能推荐 3.2 实时欺诈检测 3.3 实时数仓与 ETL 四、Flink 性能…

【论文阅读】Dolphin: Document Image Parsing via Heterogeneous Anchor Prompting

Paper&#xff1a;https://arxiv.org/abs/2505.14059 Source code: https://github.com/bytedance/Dolphin 作者机构&#xff1a;字节跳动 背景 业务场景 企业数据大多数都以文本、图片、扫描件、电子表格、在线文档、邮件等文档的形式存在&#xff0c;例如&#xff1a;PDF文…

谷歌地图免费下载手机版

软件标签: 谷歌地图 谷歌卫星高清地图 下载链接&#xff1a;夸克网盘分享 手机地图 谷歌地图免费下载(google maps)是谷歌公司打造的手机高清电子地图。2024谷歌地图官方中文版能够直观的表达出世界各地的地点&#xff0c;在地图中能够清晰的了解到自身的定位&#xff0c;让…

DeepSeek 赋能金融衍生品:定价与风险管理的智能革命

目录 一、引言1.1 金融衍生品市场发展现状1.2 DeepSeek 的技术特点和优势1.3 研究目的和意义 二、金融衍生品定价与风险管理基础2.1 金融衍生品定价常用方法2.2 金融风险管理主要策略 三、DeepSeek 在金融衍生品定价中的应用3.1 DeepSeek 助力定价模型构建3.2 案例分析&#xf…