React组件中Web Worker引发竞态问题排查求助
React Web Worker定时器多次启停后异常的问题排查与修复
问题描述
在React.js中基于Web Worker开发高性能定时器组件(用于学习),首次点击“Start”按钮时逻辑正常,但多次启停后出现异常:点击“Stop”后定时器无法停止,且运行速度越来越快。
复现步骤
| 步骤 | 操作 | 按钮状态 | 定时器状态 | 说明 |
|---|---|---|---|---|
| 1 | 点击“Start” | 文本变为“Stop”,颜色变红 | 活跃 | 定时器正常启动,速度符合预期 |
| 2 | 点击“Stop” | 文本变为“Start”,颜色变蓝 | 非活跃 | 定时器正常重置 |
| 3 | 点击“Start” | 文本变为“Stop”,颜色变红 | 活跃 | 定时器正常启动,速度符合预期 |
| 4 | 点击“Stop” | 文本变为“Start”,颜色变蓝 | 活跃! | 定时器重置为0但无法停止,速度略有加快 |
| 5 | 点击“Start” | 文本变为“Stop”,颜色变红 | 活跃! | 定时器重置为0但无法停止,速度进一步加快 |
问题根源
- Worker未清理旧定时器:每次收到
start消息时,直接创建新的setInterval,但未清除之前已存在的定时器。多次点击Start会导致多个定时器并行运行,每个定时器都会触发postMessage更新秒数,最终表现为速度叠加。 - timerId覆盖导致无法清除全部定时器:每次启动新定时器时,
timerId会被覆盖为新的定时器ID,点击Stop时只能清除最后一个创建的定时器,之前的旧定时器仍在后台运行,无法被终止。
修复方案
核心修改:Worker启动前清除旧定时器
在Worker的start分支中,先检查并清除已存在的定时器,再创建新的定时器,避免多个定时器并行运行。同时在Stop操作后重置timerId,避免无效引用。
修正后的代码
/src/workers/timer-worker.ts
const timerWorker = () => { let seconds = 0 let timerId: number | null = null const INTERVAL = 100 function dataFactory(time: number, status: "start" | "stop") { return { time, status } } self.onmessage = (event) => { if (event.data === "start") { // 启动前先清除已存在的定时器 if (timerId) { clearInterval(timerId) } seconds = 0 timerId = setInterval(() => { seconds++ self.postMessage(dataFactory(seconds, "start")) }, INTERVAL) } if (event.data === "stop") { seconds = 0 self.postMessage(dataFactory(seconds, "stop")) if (timerId) { clearInterval(timerId) timerId = null // 清除后重置timerId,避免无效引用 } } } } export default timerWorker
src/components/Timer.tsx(可选优化)
确保消息发送与状态更新的顺序,避免竞态:
function clickHandler() { setTimerRunning((prev) => { const newState = !prev const message = newState ? TimerStatus.START : TimerStatus.STOP if (workerRef.current) { workerRef.current.postMessage(message) } return newState }) }
验证效果
修改后多次启停定时器:
- 点击Stop时,所有运行的定时器都会被清除,定时器完全停止
- 每次启动时只会创建一个定时器,速度保持稳定,不会出现叠加加速的情况
内容的提问来源于stack exchange,提问作者Abhinay
相关产品推荐
相关产品推荐

