【问题标题】:How to let a webworker do multiple tasks simultaneously?如何让网络工作者同时执行多项任务?
【发布时间】:2020-09-16 10:54:25
【问题描述】:

我试图让 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】:

    您的问题是您尝试采用基于事件的 API 并将其用作基于 Promise 的 API,但事件可能会触发多次,而 Promise 应该只解析一次。

    Worker 和主线程之间的通信是通过发送和接收消息来进行的,但是这些消息之间默认没有一对一的关系。通信的两端(端口)将简单地堆叠传入的消息,并在有时间时按顺序处理它们。

    在您的代码中,f1 的主线程的 worker.onmessage 处理程序已被第二次调用 f2 同步覆盖(稍后一个微任务,但对我们而言这仍然是同步的)。
    您可以使用addEventListener 方法附加您的事件,至少这样它不会被覆盖。但即便如此,当第一个 message 事件将在 worker 上触发时,两个处理程序都会认为确实有自己的消息到达,而实际上它是 f2 的消息。 所以这不是你需要的......

    您需要建立一个通信协议,使两端能够识别每个任务。例如,您可以使用包含 .UIID 成员的对象包装所有任务的数据,确保两端都以这种方式包装它们的消息,然后从主线程检查该 UUID 以解析适当的 Promise。

    但是实现和使用可能会变得有点复杂。


    我个人最喜欢的方式是为每个任务创建一个新的MessageChannel。如果你不知道这个 API,我邀请你阅读我的 this answer 解释基础知识。

    由于我们确定唯一将通过此 MessageChannel 传递的消息是 Worker 对我们发送给它的一项任务的响应,因此我们可以像 Promise 一样等待它。 p>

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

    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>

    【讨论】:

      猜你喜欢
      • 2022-01-22
      • 2014-03-21
      • 2020-09-15
      • 1970-01-01
      • 1970-01-01
      • 2016-06-08
      • 2011-01-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多