javascript宏任务本身串行执行,所谓并发限流实为通过队列+计数器协调异步任务发起节奏,控制如fetch等请求同时发出数量不超过设定阈值。

在 JavaScript 中,宏任务(如 setTimeout、setInterval、I/O 回调、UI 渲染后回调等)本身是**串行执行**的,无法真正“并发”。所谓“宏任务中实现并发任务限流”,实际是指:**控制一批异步任务(例如多个 fetch、setTimeout 触发的逻辑)以不超过指定并发数的方式,在宏任务队列中有序、可控地执行**。核心不是让宏任务并行,而是用队列 + 令牌桶 / 计数器机制协调任务调度。
用任务队列 + 并发计数器控制执行节奏
这是最直观、易理解的方式:维护一个待执行任务队列和当前运行中的任务数,每次有任务完成就从队列里取下一个执行。
- 定义最大并发数(如
max = 3) - 用数组模拟队列,
pending存待执行函数,running记当前执行中数量 - 每个任务包装成返回 Promise 的函数,执行完调用
next()启动下一个 - 所有任务通过
add(task)入队,不立即执行
示例:
class TaskLimiter {
constructor(max) {
this.max = max;
this.pending = [];
this.running = 0;
}
add(task) {
return new Promise((resolve, reject) => {
this.pending.push({ task, resolve, reject });
this.next();
});
}
next() {
if (this.running >= this.max || this.pending.length === 0) return;
this.running++;
const { task, resolve, reject } = this.pending.shift();
task().then(resolve).catch(reject).finally(() => {
this.running--;
this.next(); // 尝试调度下一个
});
}
}
// 使用:限制最多 2 个 fetch 同时发出
const limiter = new TaskLimiter(2);
const urls = ['/api/1', '/api/2', '/api/3', '/api/4'];
urls.map(url => () =>
fetch(url).then(r => r.json())
).forEach(task => limiter.add(task));
用 Promise.allSettled + 分批调度模拟限流
适合已知全部任务列表、且希望分批次“批量提交、批量等待”的场景。把大数组切片,每批最多 max 个,前一批全结束再发下一批。
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 用
Array.prototype.reduce()或递归方式串行处理批次 - 每轮用
Promise.allSettled()等待本批全部完成(避免因某一个失败中断整体) - 注意:该方式吞吐略低(必须等整批完成),但逻辑清晰、无状态管理负担
示例:
async function limitConcurrency(tasks, max) {
const results = [];
for (let i = 0; i t()));
results.push(...batchResults);
}
return results;
}
// 调用
limitConcurrency([
() => fetch('/a').then(r => r.json()),
() => fetch('/b').then(r => r.json()),
() => fetch('/c').then(r => r.json())
], 2);
结合 requestIdleCallback 或 setTimeout 做更柔性的节流
如果任务是 CPU 密集型(如大量数据处理),或需避免阻塞主线程渲染,可在宏任务中主动让出控制权。
- 用
requestIdleCallback(兼容性注意)在浏览器空闲时执行任务片段 - 或用
setTimeout(fn, 0)把长任务拆成微小宏任务,配合计数器限流 - 关键点:每次只执行 1 个任务,执行完检查是否还能继续(如未超限且队列非空),再
setTimeout(next, 0)
这种写法更适合“后台渐进式处理”,兼顾响应性和限流目标。
注意事项与常见误区
⚠️ 宏任务本身不能并发 —— 浏览器事件循环决定了同一时刻只有一个宏任务在执行。所谓“并发”只是视觉/业务意义上的同时进行(比如 3 个 fetch 请求确实会同时发出去),但它们的回调进入宏任务队列后仍是排队执行。
- 限流对象应是“发起动作”(如调用
fetch),而不是“回调执行”;否则无法真正控制网络请求数 - 不要混淆宏任务与 Web Worker:Worker 才能真正多线程,并发限流在 Worker 内做更合理
- 若使用
async/await配合Promise控制,请确保 await 的是真正异步操作,而非同步代码 - 错误地用
while+setTimeout可能导致队列堆积或无限递归,务必用队列 + 显式调度
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










