示例成品 看全文 ↓

死循环卡死怎么排查

点某个按钮后页面直接卡死无响应,CPU 飙满,不报错也不动,只能强制刷新,不知道是哪段循环停不下来。

免费看示例成品:代码 · 文字写代码做网站 ›

有人这样说「点个按钮页面直接卡死,CPU飙到100%,啥提示都没有,只能强制刷新。」

能替你选的都选好了。不想动,直接点下面「交给 AI 办」,点一下就开始做;想改,就点一下要的那个。

已替你选好想改就点一下

只有你知道的不填就先空着,成品里留【待填】

还想怎么改可以不选

高级选项一般不用动 · 这件事通常 1 分钟左右做好

示例成品全文按上面这组示例选项真跑出来的

先纠正一下:这段不是“无限死循环”,是固定跑 10 亿次,但对浏览器主线程来说和死机没区别。

for (let i = 0; i < 1000000000; i++) { /* 没 break */ }

浏览器主线程是单线程,它一直跑这个同步循环,就没空处理点击、渲染、React/Vue 更新,所以页面直接无响应,CPU 飙到 100%。


一、怎么排查

1. 最有效:卡死时直接暂停 JS

这招对同步死循环基本一击定位。

步骤:

  1. 点击按钮前,先打开 Chrome DevTools。
  2. 点按钮,让页面卡死。
  3. 切到 DevTools → Sources。
  4. 点脚本暂停按钮,快捷键通常是 F8。
  5. 浏览器会停在当前正在执行的 JS 那一行。

你会直接看到停在:

for (let i = 0; i < 1000000000; i++) { /* 没 break */ }

调用栈会显示:

onClick -> for循环
2. 用 Performance 录长任务

步骤:

  1. DevTools → Performance。
  2. 点录制按钮。
  3. 点页面上那个按钮。
  4. 等几秒后停止录制。
  5. 找红色长任务,点进去看 Bottom-Up / Call Tree。

耗时最高的函数会指向那个 for 循环。

3. 土办法:二分注释

把可疑代码注释掉,或者把 10 亿改成 1000 试试:

for (let i = 0; i < 1000; i++) { /* ... */ }

如果不卡了,说明就是循环次数太大。


二、怎么修

不要在主线程同步跑这种大循环。

方案 A:时间分片,让页面能喘气
async function runInChunks(total, chunkSize, onProgress) {
  for (let i = 0; i < total; i += chunkSize) {
    const end = Math.min(i + chunkSize, total);

    for (let j = i; j < end; j++) {
      // 你的实际逻辑写这里,用 j 代替原来的 i
    }

    if (onProgress) onProgress(end, total);

    // 让出主线程,让浏览器处理点击、渲染
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

用法:

await runInChunks(1000000000, 1000000, (done, total) => {
  console.log(`${done}/${total}`);
});

这样每跑 100 万次就让出一次主线程,页面不会卡死。

注意:async/await 本身不能让出主线程,必须用 setTimeout 这类宏任务。


方案 B:纯计算丢 Web Worker

如果循环里是纯计算,直接丢给 Worker,主线程完全不阻塞。

// worker.js
self.onmessage = function (e) {
  let sum = 0;
  for (let i = 0; i < e.data; i++) {
    sum += i;
  }
  self.postMessage(sum);
};
// main.js
const worker = new Worker('worker.js');
worker.postMessage(1000000000);
worker.onmessage = e => {
  console.log('结果:', e.data);
};

页面该怎么操作还怎么操作,不会卡。


三、React / Vue 配合

点击后设置 loading,禁用按钮,防止用户重复点。

React
const [loading, setLoading] = useState(false);

async function handleClick() {
  setLoading(true);
  await runInChunks(1000000000, 1000000);
  setLoading(false);
}
Vue
const loading = ref(false);

async function onClick() {
  loading.value = true;
  await runInChunks(1000000000, 1000000);
  loading.value = false;
}

如果这段循环只是测试代码,直接删掉或者改小数字。10 亿次空转本身没有实际意义。

同类还能办

全部 ›
把提示词失败日志转成归因表格免费看示例 ›屎山代码怎么安全重构免费看示例 ›把报错日志转成排查步骤免费看示例 ›怎么给老代码补单元测试免费看示例 ›核对时钟配置是不是一致免费看示例 ›修补区域有没有重复纹理免费看示例 ›

同样出写代码做网站的

首页