我试图让 Web Worker 管理其状态,同时服务多个异步请求。
工人.ts 文件
let a =0; //this is my worker's state
let worker=self as unknown as Worker;
worker.onmessage =(e)=>{
console.log("Rec msg", e.data);
if(e.data === "+1"){
setTimeout(()=>{
a=a+1;
worker.postMessage(a);
},3000);
}else if(e.data=== "+2"){
setTimeout(()=>{
a=a+2;
worker.postMessage(a);
},1000)
}
}
这是我的主文件:main.ts
let w =new Worker("./worker.ts", {type: "module"})
let wf =async (op: string)=>{
w.postMessage(op);
return new Promise<any>((res,rej)=>{
w.onmessage=res;
});
}
(async()=>{
let f1 = await wf("+1");
console.log("f1",f1.data);
})();
(async()=>{
let f2 = await wf("+2");
console.log("f2",f2.data);
})()
Only f2
返回,并且f1
丢失了。
我使用超时来模拟工作人员自己完成的一些异步任务。
我如何同时收到f1
and f2
?
您的问题是,您尝试采用基于事件的 API 并将其用作基于 Promise 的 API,但事件可能会触发多次,而 Promise 应该只解析一次。
Worker 和主线程之间的通信是通过发送和接收消息来进行的,但默认情况下这些消息之间没有一对一的关系。通信的两端(端口)将简单地堆叠传入的消息,并在有时间时按顺序处理它们。
在您的代码中,主线程的worker.onmessage
的处理程序f1
已被第二次调用覆盖f2
同步(稍后一个微任务,但对我们来说仍然是同步的)。
您可以使用以下方式附加您的活动addEventListener
方法,至少这样它不会被覆盖。但即便如此,当第一个message事件将触发worker
,两个处理程序都会认为确实有自己的消息到达,而实际上它是其中之一f2
. 所以这不是你需要的......
您需要的是建立一个通信协议,使两端能够识别每个任务。例如,您可以使用包含以下内容的对象来包装所有任务的数据.UIID
成员,请确保两端都以这种方式包装消息,然后从主线程检查 UUID 以解析适当的 Promise。
但这实施和使用起来可能会变得有点复杂。
我个人最喜欢的方式是创建一个新的消息通道每个任务。如果您不知道这个 API,我邀请您阅读这个答案我的解释基础知识。
因为我们确信通过此传递的唯一一条消息消息通道是 Worker 对我们发送给它的一项任务的响应,我们可以像 Promise 一样等待它。
我们所要做的就是确保在工作线程中我们通过传输的端口而不是全局范围进行响应。
const url = getWorkerURL();
const worker = new Worker(url)
const workerFunc = (op) => {
// we create a new MessageChannel
const channel = new MessageChannel();
// we transfer one of its ports to the Worker thread
worker.postMessage(op, [channel.port1]);
return new Promise((res,rej) => {
// we listen for a message from the remaining port of our MessageChannel
channel.port2.onmessage = (evt) => res(evt.data);
});
}
(async () => {
const f1 = await workerFunc("+1");
console.log("f1", f1);
})();
(async () => {
const f2 = await workerFunc("+2");
console.log("f2", f2);
})()
// SO only
function getWorkerURL() {
const elem = document.querySelector( '[type="worker-script"]' );
const script = elem.textContent;
const blob = new Blob( [script], { type: "text/javascript" } );
return URL.createObjectURL( blob );
}
<script type="worker-script">
let a = 0;
const worker = self;
worker.onmessage = (evt) => {
const port = evt.ports[0]; // this is where we will respond
if (evt.data === "+1") {
setTimeout(() => {
a = a + 1;
// we respond through the 'port'
port.postMessage(a);
}, 3000);
}
else if (evt.data === "+2") {
setTimeout(() => {
a = a + 2;
// we respond through the 'port'
port.postMessage(a);
}, 1000)
}
};
</script>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)