我正在构建一个组件来测试不同算法的性能。该算法返回运行所花费的ms,这是我想显示的。 “ fastAlgorithm”大约需要半秒钟,而“ slowAlgorithm”大约需要5秒。
我的问题是,直到两种算法都完成后,UI才会重新呈现结果。我想在快速算法完成时立即显示结果,在慢速算法完成时显示结果。
我已经阅读了有关React批处理在重新渲染之前如何更新的信息,但是有什么方法可以改变这种行为?还是有更好的方法来组织我的组件来实现我想要的?
我正在使用反应16.13.1
这是我的组成部分:
import { useState } from 'react'
import { fastAlgorithm, slowAlgorithm } from '../utils/algorithms'
const PerformanceTest = () => {
const [slowResult, setSlowResult] = useState(false)
const [fastResult, setFastResult] = useState(false)
const testPerformance = async () => {
fastAlgorithm().then(result => {
setFastResult(result)
})
slowAlgorithm().then(result => {
setSlowResult(result)
})
}
return (
<div>
<button onClick={testPerformance}>Run test!</button>
<div>{fastResult}</div>
<div>{slowResult}</div>
</div>
)
}
export default PerformanceTest
我在某处读到ReactDOM.flushSync()会在每次状态更改时触发重新渲染,但没有任何区别。这是我尝试过的:
const testPerformance = async () => {
ReactDOM.flushSync(() =>
fastAlgorithm().then(result => {
setFastResult(result)
})
)
ReactDOM.flushSync(() =>
slowAlgorithm().then(result => {
setSlowResult(result)
})
)
}
还有这个:
const testPerformance = async () => {
fastAlgorithm().then(result => {
ReactDOM.flushSync(() =>
setFastResult(result)
)
})
slowAlgorithm().then(result => {
ReactDOM.flushSync(() =>
setSlowResult(result)
)
})
}
我也尝试过重组算法,以便他们不使用Promises并尝试这样做,但是没有运气:
const testPerformance = () => {
setFastResult(fastAlgorithm())
setSlowResult(slowAlgorithm())
}
修改
正如 Sujoy Saha 在下面的评论中建议的那样,我使用setTimeout()将算法替换为简单的算法,并且一切正常。首先显示“快速”,然后两秒钟后显示“慢”。
但是,如果我做下面的代码这样的事情是行不通的。当较慢的功能完成时,“ Fast”和“ Slow”都会显示出来。有人知道确切的时间/方式如何在React中进行批量渲染以及如何避免这种情况吗?
export const slowAlgorithm = () => {
return new Promise((resolve, reject) => {
const array = []
for(let i = 0; i < 9000; i++) {
for(let y = 0; y < 9000; y++) {
array.push(y);
}
}
resolve('slow')
})
}
答案 0 :(得分:0)
您最初的 PerfomanceTest 组件是正确的。组件将针对每个状态更改重新呈现。我认为问题出在您的算法中。请让我们知道您是如何在那儿退还诺言的。 请遵循以下代码段供您参考。
export const fastAlgorithm = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('fast')
}, 1000)
})
}
export const slowAlgorithm = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('slow')
}, 3000)
})
}
答案 1 :(得分:0)
您是否在主线程上同步运行算法?如果是这样,那可能是阻止React重新渲染的原因。您可能需要将它们移至worker threads。
下面的内容大致基于this answer,减去了所有兼容性(假设您不需要IE支持):
cut -d\" \"
请注意,// Note that `args` must each be JSON serializable,
// due to how we're creating the worker string.
// If there are no arguments, that's fine too (`args` is then [])
const asyncify = fn => (...args) => {
const workerStr = `
const fn = ${fn.toString()}
const start = new Date().valueOf()
self.onmessage = () => {
const returnVal = fn(${args.map(JSON.stringify).join(",")})
const executionTimeMs = new Date().valueOf() - start
self.postMessage({ returnVal, executionTimeMs })
}
`
const blob = new Blob([workerStr], { type: "application/javascript" })
const worker = new Worker(URL.createObjectURL(blob))
const promise = new Promise((resolve, reject) => {
worker.onmessage = (result) => {
resolve(result)
worker.terminate()
}
worker.onerror = (err) => {
reject(err)
worker.terminate()
}
})
worker.postMessage(null)
// in case we need it for React cleanup later
promise.abort = worker.terminate.bind(worker)
return promise
}
const slowAlgorithm = (outerIterations, innerIterations) => {
const array = []
for (let i = 0; i < outerIterations; i++) {
for (let y = 0; y < innerIterations; y++) {
array.push(y)
}
}
return array.length
}
const slowAlgorithmAsync = asyncify(slowAlgorithm)
// rendering not blocked - just pretend this is React
const render = x =>
document.write(`<pre>${JSON.stringify(x, null, 2)}</pre>`)
slowAlgorithmAsync(9000, 9000)
.then(({ data }) => render(data))
slowAlgorithmAsync(100, 100)
.then(({ data }) => render(data))
实际上是在此处的工作线程上下文中被fn
编辑的,因此您需要确保代码受信任。假定您已经很乐意在主线程上运行它了。