如何让一个webworker同时做多个任务?

问题描述 投票:0回答:1

我想让一个Web-Worker管理自己的状态,同时为多个异步请求提供服务。

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);
})()

只有 f2 返回,并且 f1 丢失.我已经使用超时来模拟比如说一些异步任务由工人自己完成。

我怎样才能同时收到 f1f2?

javascript node.js typescript web-worker deno
1个回答
4
投票

你的问题是,你试图将一个基于事件的API作为一个基于Promise的API使用,但事件可能会多次触发,而Promise应该只解析一次。

Worker和主线程之间的通信是通过发送和接收消息来实现的,但默认情况下,这些消息之间没有一对一的关系。通信的两端(端口)会简单地堆叠接收到的消息,并在有时间的时候依次处理它们。

在你的代码中,主线程的 worker.onmessage 的处理者 f1 已被第二个调用覆盖 f2 同步(一个微任务之后,但这对我们来说仍然是同步的)。你可以在事件中使用 addEventListener 方法,至少这样就不会被覆盖。但即便如此,当第一个 讯息 事件将在 worker,两个处理者都会认为是自己的信息到达了,而事实上是自己的信息到达了。f2. 所以这不是你需要的... ...

你需要的是建立一个通信协议,允许两端识别每个任务。例如,你可以用一个包含以下内容的对象来包装你所有任务的数据 .UIID 成员,要确保两端都这样封装消息,然后从主线程检查该UUID来解析相应的Promise。

但是这样实现和使用起来会变得有点复杂。


我个人最喜欢的方式是在主线程中创建一个新的 消息频道 每个任务。如果你不知道这个API,我邀请你读一读 本回答 解释我的基础知识。

由于我们确信只有一个信息会通过这个。消息频道 是Worker对我们发送给它的任务的响应,我们可以像等待Promise一样等待它。

我们所要做的,就是确保在 Worker 线程中,我们通过传输的端口而不是全局范围来响应。

const url = getWorkerURL();
const w = new Worker(url)

let wf =async (op)=>{
  // we create a new MessageChannel
  const channel = new MessageChannel();
  // we transfer one of its ports to the Worker thread
  w.postMessage(op, [channel.port1]);

  return new Promise((res,rej)=>{
    // we listen for a message from the remaining port of our MessageChannel
    channel.port2.onmessage=res;
  });
}

(async()=>{
    let f1 = await wf("+1");
    console.log("f1",f1.data);
})();

(async()=>{
    let f2 = await wf("+2");
    console.log("f2",f2.data);
})()


// 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 =(e)=>{
  const port = e.ports[0]; // this is where we will respond
  if(e.data === "+1"){
    setTimeout(()=>{
      a=a+1;
      // we respond through the 'port'
      port.postMessage(a);
    },3000);
  }else if(e.data=== "+2"){
    setTimeout(()=>{
      a=a+2;
      // we respond through the 'port'
      port.postMessage(a);
    },1000)
  }
};
</script>
© www.soinside.com 2019 - 2024. All rights reserved.