createAsyncThunk pending状态更新后未立即触发React组件重渲染问题
问题原因
- 问题根源是JavaScript单线程执行特性导致的主线程阻塞:
createAsyncThunk派发异步action时,会先同步触发pendingreducer修改store状态为loading- 你实现的
getConfidenceIntervals函数中,new Promise的构造回调是同步执行的,会立刻调用doSomeHeavyComputation重度计算,直接占用主线程 - 浏览器重渲染属于宏任务,需要等当前所有同步任务执行完成后才会触发,因此pending状态更新对应的组件重渲染被阻塞到计算完成后才执行
- 计算完成后紧接着触发
fulfilledreducer修改状态为idle,主线程空闲后会连续处理两次状态更新的重渲染,因此看不到中间的loading状态。
解决方案
方案1:延后计算执行,让出主线程先渲染
修改getConfidenceIntervals,将重度计算逻辑放到下一个宏任务中执行,给浏览器留出时间渲染loading状态:
export function getConfidenceIntervals(totalRuns: number, itemsTarget: number, throughputs: number[], startingDate: Date) { return new Promise<Map<string, number>>((resolve) => { console.log('beginning'); // 延时0ms执行计算,让出主线程 setTimeout(() => { const outcomes = doSomeHeavyComputation(totalRuns, itemsTarget, throughputs, startingDate); console.log('ending'); resolve(outcomes); }, 0); }); }
也可以在createAsyncThunk的payloadCreator开头加一行延时逻辑,效果一致:
export const computeConfidenceIntervalsAsync = createAsyncThunk( 'forecast/getConfidenceIntervals', async (data: ConfidenceIntervalsParams) => { // 先让出主线程完成loading渲染 await new Promise(resolve => setTimeout(resolve, 0)); const response = await getConfidenceIntervals(data.totalRuns, data.itemsTarget, data.throughputs, new Date(data.startingDate)); return [...response.entries()]; } );
方案2:使用Web Worker处理重度计算
如果计算逻辑复杂度极高、执行时间长,建议将doSomeHeavyComputation逻辑放到Web Worker线程中执行,完全不占用主线程,不仅能正常渲染loading状态,也不会导致页面交互卡顿。
内容的提问来源于stack exchange,提问作者commonUser
相关产品推荐
相关产品推荐

