【发布时间】:2021-10-13 10:39:06
【问题描述】:
我有一个带有状态变量 jobs 的 React 组件。当状态变量ready 为真时,它应该开始由 Web Worker 执行作业(一次一个)。
import React, { useEffect, useRef } from 'react'
// create webworker
const job_worker = new Worker("worker.bundle.js", { type: "module" });
function App() {
const [jobs, set_jobs] = React.useState([
{ ... },
{ ... },
])
const [ready, set_ready] = React.useState(false)
// start worker loop
useEffect(() => {
const worker_loop = async () => {
setTimeout(async () => {
// check if ready to execute a job
if (ready) { // <== suffers from 'stale closure'
// grab a job
const job = jobsRef.current.find(j => !j.done)
// listen for webworker results
job_worker.onmessage = (e) => {
console.log("received response from webworker: '", e.data, "'")
// SET RESULT IN JOB
// job is handled by worker; now start the worker_loop again
return worker_loop()
}
// post job to worker
job_worker.postMessage({job: job})
return // do not continue; 'onmessage' will continue the loop
}
return worker_loop()
}, 1000)
}
// start worker_loop
worker_loop()
}, [])
return (
<div>
{/* code to add jobs and set ready state */}
</div>
);
}
我尝试通过使用(无限)worker_loop 来做到这一点,它在 React 组件挂载时启动(使用 useEffect)。循环有点工作,但worker_loop 中的ready 变量保持在初始状态值(称为“陈旧关闭”问题)。 jobs 状态变量可能相同。
我已经按照here 的建议尝试使用“createRef”。但问题仍然存在。我也觉得有一个更简单的解决方案。
有没有更好的方法来处理 React 状态变量中的“作业”?某种可以访问 React 组件的“job-runner 进程/功能”。顺便说一句,我没有义务使用 WebWorker。
【问题讨论】:
-
为什么要使用 setTimeout?使用其他东西而不是将整个逻辑包装在 setTimeout 中(因此我遇到了疯狂的关闭问题)也在依赖数组中传递“就绪”。这很可能会解决您的关闭问题。
-
我觉得很有挑战性。如果我必须实现一个workerin react,我会尝试一个useContext,它在userRrducer中定义了一个动作。为了最大限度地控制并避免由于组件被破坏或其他状态发生变化而导致的突变。我相信我不会尝试。不能运行worker服务器端吗?
-
鉴于 worker 是单一且全局的,我认为使用一些顶级 JS 代码(不在 React 中,但可从 React 中调用)来控制它更有意义,并使用一系列作业运行,当前正在运行的作业等。这样,您可以创建一个函数,该函数返回运行作业的承诺,您可以在 React 中通过例如
useAsync.
标签: javascript reactjs react-hooks web-worker