一、典型并发场景
-
初始化加载场景
页面启动时需同时请求用户信息、配置数据、多模块内容等,导致瞬间高并发请求
示例:电商首页需加载用户登录态、推荐商品、广告位等10+接口 -
批量操作场景
用户执行批量下载/删除操作时触发大量并行请求,如选中100个文件执行批量删除
示例:管理后台批量删除500条订单记录 -
实时数据场景
高频轮询或WebSocket推送引发持续并发,如股票行情、在线协作编辑的频繁状态同步 -
级联请求场景
前序请求结果触发后续多个并行请求,如选择省份后需同时加载城市列表和区域统计数据
二、核心解决方案
场景类型 | 推荐方案 | 优势 |
后台管理系统表格 | 传统分页方案 | 明确的页码导航 |
社交媒体信息流 | 无限滚动+IntersectionObserver | 自然浏览体验 |
电商商品列表 | 虚拟滚动+图片懒加载 | 高性能处理百万级数据 |
单页应用内容区块 | 组件级懒加载 | 按需加载提升首屏速度 |
(一)流量控制策略
-
并发队列控制
function fetchData(id) { // 修改为接收id参数 return new Promise(resolve => { const delay = Math.random() * 1000 + 500; // 随机延迟500-1500ms setTimeout(() => { resolve({ id, delay: delay.toFixed(0) + 'ms' }); }, delay); }); } class ConcurrencyController { constructor(max = 6) { this.taskQueue = []; this.activeCount = 0; this.maxConcurrency = max; } add(caller) { return new Promise((resolve, reject) => { const task = this._createTask(caller, resolve, reject); if (this.activeCount >= this.maxConcurrency) { this.taskQueue.push(task); } else { task(); } }); } _createTask(caller, resolve, reject) { return () => { this.activeCount++; // 正确位置:任务开始时立即增加计数 caller() .then(resolve) .catch(reject) .finally(() => { this.activeCount--; if (this.taskQueue.length > 0) { const nextTask = this.taskQueue.shift(); nextTask(); // 自动执行下一个任务 } }); }; } } let controller=new ConcurrencyController(2) const tasks = [ controller.add(() => fetchData(1)), controller.add(() => fetchData(2)), controller.add(() => fetchData(3)), controller.add(() => fetchData(4)), controller.add(() => fetchData(5)), controller.add(() => fetchData(6)) ]; Promise.all(tasks) .then(results => { console.log('最终完成顺序:', results); console.log('预期输出顺序: [1,2,3,4,5,6] 的实际完成顺序可能不同'); });
核心机制解析
// 简化后的代码逻辑
add(caller) {
return new Promise((outerResolve, outerReject) => {
const task = () => {
caller() // 1. 执行原始异步任务
.then(outerResolve) // 2. 将结果传递给外层Promise
.catch(outerReject) // 3. 错误冒泡
}
})
}
值传递流程
步骤1:任务执行
caller() // 执行返回Promise的异步任务,例如:
// fetchData("url") → Promise<{data:"模拟数据"}>
步骤2:结果传递
.then(outerResolve)
// 等价于:
.then(result => outerResolve(result))
步骤3:最终结果
// add()返回的Promise将获得caller()的结果
controller.add(() => fetchData(url))
.then(data => {
// data = {data:"模拟数据"}
})
使用并发控制库
- 采用第三方库如
p-limit
快速实现并发控制,避免重复造轮子38 - 实现示例:
import pLimit from 'p-limit';
const limit = pLimit(6);
const tasks = urls.map(url => limit(() => fetch(url)));
防抖和节流
传统分页实现(页码)
let currentPage = 1
const pageSize = 20
// 核心加载函数
async function loadPage(page) {
try {
const res = await fetch(`/api/data?page=${page}&size=${pageSize}`)
const data = await res.json()
renderList(data.items)
document.getElementById('pageNum').textContent = page
} catch(err) {
console.error('加载失败', err)
}
}
// 事件绑定
document.getElementById('prev').addEventListener('click', () => {
if(currentPage > 1) loadPage(--currentPage)
})
document.getElementById('next').addEventListener('click', () => {
loadPage(++currentPage)
})
// 初始化加载
loadPage(1)
无限滚动分页(懒加载)
let isLoading = false
let currentPage = 1
const threshold = 200 // 距离底部200px触发加载
window.addEventListener('scroll', () => {
const { scrollTop, clientHeight, scrollHeight } = document.documentElement
// 到达触发点且未处于加载状态
if(scrollTop + clientHeight >= scrollHeight - threshold && !isLoading) {
loadMoreData()
}
})
async function loadMoreData() {
isLoading = true
try {
const res = await fetch(`/api/data?page=${currentPage}`)
const data = await res.json()
appendItems(data.items)
currentPage++
if(data.hasMore) showLoadingTip()
} catch(err) {
console.error('加载失败', err)
} finally {
isLoading = false
}
}
// 初始加载
loadMoreData()
三、组件级懒加载(React示例)
import { useState, useEffect } from 'react'
function LazyList() {
const [data, setData] = useState([])
const [page, setPage] = useState(1)
const [loading, setLoading] = useState(false)
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
if(entries.isIntersecting && !loading) {
loadMore()
}
}, { threshold: 0.1 })
observer.observe(document.querySelector('#sentinel'))
return () => observer.disconnect()
}, [])
const loadMore = async () => {
setLoading(true)
try {
const res = await fetch(`/api/data?page=${page}`)
const newData = await res.json()
setData(prev => [...prev, ...newData.items])
setPage(p => p + 1)
} finally {
setLoading(false)
}
}
return (
<div>
{data.map(item => <div key={item.id}>{item.name}</div>)}
<div id="sentinel">
{loading ? '加载中...' : '滚动加载更多'}
</div>
</div>
)
}
图片懒加载优化
<img data-src="real-image.jpg" class="lazy-img" />
<script>
// 原生JS实现
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
observer.unobserve(img)
}
})
}, { rootMargin: '200px' })
document.querySelectorAll('.lazy-img').forEach(img => observer.observe(img))
</script>
关键优化点说明
- 防抖处理 - 滚动事件需添加防抖避免高频触发
function debounce(fn, delay=200) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), delay)
}
}
2.虚拟列表优化 - 超长列表建议使用虚拟滚动方案(如react-window库)