长任务指执行超50ms的同步JS代码,会阻塞主线程;分片队列通过requestIdleCallback或Promise微任务将其拆解为小任务,在空闲时段执行以避免卡顿。

长任务(Long Task)指执行时间超过 50ms 的同步 JavaScript 代码,会阻塞主线程,导致页面卡顿、响应延迟。分片队列(Chunked Queue)是一种将长任务拆成多个小任务、在空闲时段依次执行的优化策略,核心是利用 requestIdleCallback 或 setTimeout/Promise.resolve() 让出主线程控制权,避免连续占用。
为什么不能直接循环执行?
比如遍历 10 万条数据做复杂计算,若写成 for (let i = 0; i ,浏览器无法中断该同步循环,用户交互(点击、滚动)会被冻结,触发 Long Task 告警,LCP、INP 等核心指标恶化。
用 requestIdleCallback 实现分片队列
这是最符合“空闲调度”语义的方式,浏览器会在帧末尾有空余时间时调用回调,并提供剩余空闲时间(deadline.timeRemaining()),适合精细控制每片执行时长。
示例:将一个大数据处理任务分片执行
立即学习“Java免费学习笔记(深入)”;
function createChunkedQueue(tasks, options = {}) {
const { timeout = 30 } = options; // 单片最大执行时间(ms)
let queue = [...tasks];
function processChunk(deadline) {
while (queue.length > 0 && deadline.timeRemaining() > 0) {
const task = queue.shift();
task(); // 执行单个任务
}
if (queue.length > 0) {
requestIdleCallback(processChunk, { timeout });
} else {
console.log('所有任务执行完成');
}
}
requestIdleCallback(processChunk, { timeout });
}
// 使用示例
const bigData = Array.from({ length: 50000 }, (_, i) => i);
const tasks = bigData.map(item => () => heavyCalculation(item));
createChunkedQueue(tasks, { timeout: 25 });
兼容性 fallback:用 Promise.resolve() 微任务分片
当 requestIdleCallback 不可用(如旧版 Safari)或需更确定的调度节奏时,可用 Promise.resolve().then() 将任务切片推入微任务队列,每次只执行一部分,让渲染和事件有机会插入。
- 每轮最多执行 N 个子任务(例如 20 个),防止单次微任务过长
- 用
setTimeout(..., 0)可降级为宏任务,适合更宽松的节流 - 注意避免频繁创建 Promise 导致内存压力,可复用 resolve 函数
关键细节与避坑点
- 别在分片中修改正在遍历的原始数组——应操作副本或用索引偏移,否则逻辑错乱
- 保持状态可恢复:记录已处理数量、中间结果,意外中断后能续跑(尤其配合用户交互暂停/恢复)
- 避免嵌套分片:外层用 idle,内层再用 setTimeout 容易失控;统一用一种机制
- 监控实际分片效果:用 Performance Observer 捕获 Long Task,验证是否降至 50ms 以下



















