
在秒杀、抢购、票务开售等大流量场景下,前端会面对瞬时并发涌入、用户高频点击、网络抖动与后端限流等复杂情况。本文从踩坑角度整理前端侧的通用应对策略与落地代码片段,帮助减少重复提交、抖动请求、级联雪崩和页面卡顿。
inflight 映射,后续并发请求直接复用首个 Promise。AbortController 主动取消过期请求,搭配自定义超时器。const inflight = new Map();
function keyOf(url, opts) {
return `${url}::${JSON.stringify(opts || {})}`;
}
async function fetchOnce(url, opts) {
const key = keyOf(url, opts);
if (inflight.has(key)) return inflight.get(key);
const p = fetch(url, opts).finally(() => inflight.delete(key));
inflight.set(key, p);
return p;
}class Semaphore {
constructor(max = 2) { this.max = max; this.cur = 0; this.q = []; }
async acquire() { return new Promise(r => { this.cur < this.max ? (this.cur++, r()) : this.q.push(r); }); }
release() { const n = this.q.shift(); n ? n() : this.cur--; }
}
const sem = new Semaphore(1); // 关键路径串行
async function guarded(fn) {
await sem.acquire();
try { return await fn(); } finally { sem.release(); }
}function withTimeout(ms, controller) {
const id = setTimeout(() => controller.abort(), ms);
return () => clearTimeout(id);
}
async function request(url, opts = {}, timeout = 5000) {
const c = new AbortController();
const clear = withTimeout(timeout, c);
try { return await fetch(url, { ...opts, signal: c.signal }); }
finally { clear(); }
}async function retry(fn, { tries = 4, base = 200 } = {}) {
let attempt = 0;
while (true) {
try { return await fn(); }
catch (e) {
attempt++;
if (attempt >= tries) throw e;
const jitter = Math.random() * base;
const delay = Math.min(3000, base * 2 ** (attempt - 1) + jitter);
await new Promise(r => setTimeout(r, delay));
}
}
}class TokenBucket {
constructor({ rate = 1, burst = 3 }) {
this.rate = rate; this.burst = burst; this.tokens = burst; this.last = performance.now();
}
tryTake() {
const now = performance.now();
const dt = (now - this.last) / 1000;
this.tokens = Math.min(this.burst, this.tokens + dt * this.rate);
this.last = now;
if (this.tokens >= 1) { this.tokens -= 1; return true; }
return false;
}
}
const bucket = new TokenBucket({ rate: 0.5, burst: 2 });
function guardedClick(handler) {
return (...args) => bucket.tryTake() && handler(...args);
}let lock = false;
async function submitOnce(doSubmit) {
if (lock) return;
lock = true;
try {
// 乐观更新开始
setSubmitting(true);
const res = await doSubmit();
// 成功:保持状态
return res;
} catch (e) {
// 失败:回滚 UI
setSubmitting(false);
throw e;
} finally { lock = false; }
}前端在秒杀等高并发场景的核心目标是“控流、收敛、可取消、可回滚”。通过请求去重、并发上限、取消与超时、退避重试、幂等与乐观更新,配合后端令牌与幂等接口,可以显著降低风暴式请求与一致性问题,提升用户体验与系统稳定性。