含while循环的React useEffect执行时序疑问:为何2在3、4之后打印?
问题解答:useEffect回调打印顺序异常的原因
这个问题的核心在于React的调度机制与浏览器事件循环的交互,以及主线程阻塞对任务优先级的影响:
1. 关键概念铺垫
- 同步代码:立即执行,阻塞主线程直到执行完毕。
- 微任务:Promise回调、MutationObserver等,同步代码执行完后会被优先批量处理。
- 宏任务:setTimeout、setInterval、I/O操作等,微任务处理完毕后才会开始执行。
- React调度器(Scheduler):React内置的异步任务调度工具,会根据主线程繁忙程度动态调整任务优先级。
2. 带while循环的执行流程
// 同步代码阶段 console.log(1); // 打印1 // 阻塞主线程50ms,浏览器无法处理任何其他任务 const start = Date.now(); while (Date.now() - start < 50) { window.timestamp = Date.now(); } // React组件渲染完成进入commit阶段,将useEffect回调调度为低优先级异步任务 useEffect(() => console.log(2), [state]); // 向微任务队列添加任务 Promise.resolve().then(() => console.log(3)); // 向宏任务队列添加任务 setTimeout(() => console.log(4), 0);
同步代码执行完毕后,事件循环按如下顺序处理任务:
- 优先清空微任务队列:执行Promise回调,打印3。
- 处理宏任务队列:执行setTimeout回调,打印4。
- 主线程彻底空闲后,React调度器的低优先级任务(useEffect回调)才会执行,打印2。
3. 移除while循环的执行流程
去掉while循环后,同步代码瞬间执行完毕,主线程处于空闲状态:
- React调度器会立即执行useEffect回调(此时任务优先级足够高),打印2。
- 清空微任务队列:执行Promise回调,打印3。
- 处理宏任务队列:执行setTimeout回调,打印4。
总结
- 主线程被长时间阻塞(while循环)时,React会将useEffect回调降级为低优先级任务,推迟到微任务、宏任务之后执行。
- 主线程空闲时,React会优先调度useEffect回调,使其先于微任务、宏任务执行。
内容的提问来源于stack exchange,提问作者Janice Zhong
相关产品推荐
相关产品推荐

