我有一个简单的React类,展示
我有一个计算密集型的函数
有没有办法以非阻塞方式运行
this.state.progress
(一个数字),并且可以通过updateProgress(progress)
函数更新这个状态。class App extends React.Component {
constructor(props) {
super(props);
this.state = {
progress: 0,
}
}
updateProgress = (progress) => {this.setState({progress}); };
render() {
let {progress} = this.state;
return <h1>{progress}</h1>;
}
}
我有一个计算密集型的函数
myHeavyFunc
,需要显示进度条。我使用循环变量在myHeavyFunc
内调用上述提到的updateProgress
函数。myHeavyFunc = async (updateProgress) => {
let loopLength = 1000000;
updateProgress(0);
for(let i=0; i<loopLength; i++) {
// some processing happens here
updateProgress((i+1)/loopLength);
}
}
发生的事情是状态被更新,我可以通过在setState
回调中记录进度来确认,但组件直到最后才重新渲染。然而,如果我包括一个小的sleep 1ms,则重新渲染会发生,进度更新(显然时间大大损失,我不喜欢)。
JSFiddle here。这里我在点击进度数字时运行myHeavyFunc
。您可以看到当注释掉await sleep(1)
时,onClick
在一秒钟内完成,但未显示进度。它甚至不会为任何后续点击更改。另一方面,如果没有注释,我会获得进度更新,但只是需要很长时间才能完成!
setState
函数,但我需要在状态设置后重新渲染(至少仅在进度元素上)。如果由于批处理而丢失了几个进度更新,我可以接受,但我确实希望它能显示进度。有没有办法以非阻塞方式运行
myHeavyFunc
并在UI中更新进度?在React中更新计算密集型函数的进度的正确方法是什么?
async
并不能产生任何神奇的效果。它只是使其返回一个Promise,但如果在返回之前它执行了一些长时间且同步的操作,那么它仍然会在React有机会执行任何操作之前完成运行。 - Robin Zigmondawait sleep(1)
调用的方法是做这件事情的“正确方式”- 我很欣赏这种方式,即使在这种情况下它会使过程变得太长,那么只需在循环的每100步或每1000步调用一次即可。我假设这是基于一个比空循环更现实的例子 - 在这种情况下,如果您展示实际函数或至少更接近实际函数的近似函数,则可能会获得更好的响应。 - Robin ZigmondsetTimeout
或类似方法安排计算的下一部分。(理论上,Web Worker可能是另一种方法,但我对此并不了解。)如果您已经这样做了,那么每次都可以轻松更新一些状态,以更新进度条。 - Robin Zigmond