如何延迟 CPU 密集任务以允许渲染发生

How to delay CPU intensive tasks to allow render to happen

我有一个 React 应用程序,其中发生了多个顺序 CPU 密集型任务。我想跟踪整体操作的进度。

class Hello extends React.Component {

  constructor() {
    super()
    this.state = {
      status: 'not started'
    }
    this.handler = this.handler.bind(this)
  }
  
  componentDidUpdate() {
    if(this.state.status === 'starting') {
      this.cpuIntensive()
      this.setState({status: 'first task done'})
    }
    if(this.state.status === 'first task done') {
      this.cpuIntensive()
      this.setState({status: 'second task done'})
    }
    if(this.state.status === 'second task done') {
      this.cpuIntensive()
      this.setState({status: 'finished'})
    }
  }
  
  cpuIntensive() {
    let result = 0
    for (var i = Math.pow(7, 8); i >= 0; i--) {  
      result += Math.atan(i) * Math.tan(i);
    }
  }
  
  handler() {
    this.setState({status: 'starting'})
  }

  render() {
    return (
      <div>
        <div>{this.state.status}</div>
        <button onClick={this.handler}>START</button>
      </div>
      )
  }
}

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>

在此代码段中,当您单击 START 时,状态确实会发生变化,我们会经历 componentDidUpdate 但组件不会重新呈现。

为了让它像我想要的那样工作,我可以这样做:

class Hello extends React.Component {

  constructor() {
    super()
    this.state = {status: 'not started'}
    this.handler = this.handler.bind(this)
  }
  
  componentDidUpdate() {
    if(this.state.status === 'starting') {
     setTimeout(() => {
       this.cpuIntensive()
       this.setState({status: 'first task done'})
      }, 100)
    }
    if(this.state.status === 'first task done') {
     setTimeout(() => {
        this.cpuIntensive()
        this.setState({status: 'second task done'})
      }, 100)
    }
    if(this.state.status === 'second task done') {
     setTimeout(() => {
        this.cpuIntensive()
        this.setState({status: 'finished'})
      }, 100)
    }
  }
  
  cpuIntensive() {
    let result = 0
    for (var i = Math.pow(7, 8); i >= 0; i--) {  
      result += Math.atan(i) * Math.tan(i);
    }
  }
  
  handler() {
   this.setState({status: 'starting'})
  }

  render() {
     return (
      <div>
        <div>{this.state.status}</div>
        <button onClick={this.handler}>START</button>
      </div>
      )
  }
}

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>

IMO 看起来很糟糕。我正在寻找替代品:)

感谢您的宝贵时间。

编辑:网络工作者不是一个选项

您可以在 handler 中开始您的操作,并使用 setTimeout() 链接/允许渲染在两者之间发生。那么你就不需要使用反应状态来启动那些连续的操作。

另一种选择是使用 WebWorker。