如何让 child worker 与多个 worker 一起向 Deno 中的 parent 发送消息?

How to get child worker send message to parent in Deno with multiple workers?

我是 workers 的新手,所以根据 问题,我正在尝试以下操作:

// main.t
for(let idx = 0; idx < 4; idx++){
  const worker = new Worker(new URL('./worker.ts', import.meta.url).href, {
    type: "module", 
    deno: { namespace: true},
  });

  worker.postMessage({ id: idx, name: 'text1', email: 'ea@da.com' });
  worker.addEventListener('message', message => {
    console.log('response', message.data)
  })
}

// worker.ts
self.onmessage = async (params) => {
  const data = params.data
  console.log('params', data)
  data.name = 'text2'
  self.postMessage(data)
  self.close()
}

期待这个:

params { id: 2, name: "text1", email: "ea@da.com" }
response { id: 2, name: "text2", email: "ea@da.com" }
params { id: 0, name: "text1", email: "ea@da.com" }
response { id: 0, name: "text2", email: "ea@da.com" }
params { id: 1, name: "text1", email: "ea@da.com" }
response { id: 1, name: "text2", email: "ea@da.com" }
params { id: 3, name: "text1", email: "ea@da.com" }
response { id: 3, name: "text2", email: "ea@da.com" }

但我得到的是:

params { id: 0, name: "text1", email: "ea@da.com" }
params { id: 1, name: "text1", email: "ea@da.com" }
params { id: 2, name: "text1", email: "ea@da.com" }
params { id: 3, name: "text1", email: "ea@da.com" }
response { id: 3, name: "text2", email: "ea@da.com" }

有人可以帮我解决我哪里出错了吗?为什么我只收到最后一个工作人员的回复,而不是每个工作人员的回复?

Can someone help me with where I'm going wrong? Why I'm only getting responses from the last worker instead of getting a response from every worker?

我怀疑这是因为您在 postMessage 完成之前关闭了每个工作程序。在你的 worker.ts:

self.postMessage(data)
self.close()

来自 MDN 页面 DedicatedWorkerGlobalScope.close():

The close() method of the DedicatedWorkerGlobalScope interface discards any tasks queued in the DedicatedWorkerGlobalScope's event loop, effectively closing this particular scope.

下面,我对您的示例进行了重构,增加了一些类型安全性和更详细的日志记录,以便您可以看到更多事件和一些计时信息。在示例中,不是从每个工作人员内部调用 self.close(),而是 Worker.terminate() is called in the main scope's message event handler after receiving each message (after we're done with the worker). You can ignore the triple-slash directives(尽管它们可能有助于在您的编辑器中进行类型检查)。

util.ts:

export type ExampleData = {
  sender: string;
  timestamp: number;
};

export function formatMessage (
  direction: 'in' | 'out',
  callerName: string,
  interlocutorName: string,
  timestamp: number,
): string {
  // 7 === Math.max('receive'.length, 'post'.length);
  const directionString = (direction === 'in' ? 'receive' : 'post').padEnd(7);
  // 8 === Math.max('worker n'.length, 'main'.length);
  return `${(callerName).padEnd(8)}  ${directionString}  ${(interlocutorName).padEnd(8)}  ${timestamp}`;
}

worker.ts:

/// <reference no-default-lib="true" />
/// <reference lib="deno.worker" />

import {ExampleData, formatMessage} from './util.ts';

function handleMessageEvent (ev: MessageEvent<ExampleData>): void {
  console.log(formatMessage('in', self.name, ev.data.sender, ev.data.timestamp));

  const data: ExampleData = {sender: self.name, timestamp: Date.now()};
  self.postMessage(data);
  console.log(formatMessage('out', self.name, ev.data.sender, data.timestamp));
}

self.addEventListener('message', handleMessageEvent);

main.ts:

// /// <reference lib="deno.unstable" />

import {ExampleData, formatMessage} from './util.ts';

function handleMessageEvent (this: Worker, ev: MessageEvent<ExampleData>): void {
  console.log(formatMessage('in', 'main', ev.data.sender, ev.data.timestamp), '✅');
  this.terminate();
}

// initialize static data once outside the loop
const specifier = new URL('./worker.ts', import.meta.url).href;
const options: WorkerOptions = {
  type: 'module',
  // deno: {namespace: true}, // requires deno run --unstable
};

for (let idx = 0; idx < 4; idx += 1) {
  const workerName = `worker ${idx}`;
  const worker = new Worker(specifier, {...options, name: workerName});

  // in testing this example, registering the event listener after posting
  // the first message didn't seem to chnage behavior, however it's safer to
  // register before posting in order to ensure that registation happens
  // prior to the event generated by the response
  worker.addEventListener('message', handleMessageEvent);

  const data: ExampleData = {sender: 'main', timestamp: Date.now()};
  worker.postMessage(data);
  console.log(formatMessage('out', 'main', workerName, data.timestamp));
}

运行 示例:

/home/jesse/worker-example$ deno run --allow-read=. main.ts
Check file:///home/jesse/worker-example/main.ts
main      post     worker 0  1627390801087
Check file:///home/jesse/worker-example/worker.ts
main      post     worker 1  1627390801104
Check file:///home/jesse/worker-example/worker.ts
main      post     worker 2  1627390801122
Check file:///home/jesse/worker-example/worker.ts
main      post     worker 3  1627390801144
Check file:///home/jesse/worker-example/worker.ts
worker 0  receive  main      1627390801087
worker 0  post     main      1627390801776
main      receive  worker 0  1627390801776 ✅
worker 1  receive  main      1627390801104
main      receive  worker 1  1627390801811 ✅
worker 1  post     main      1627390801811
worker 3  receive  main      1627390801144
main      receive  worker 3  1627390801873 ✅
worker 3  post     main      1627390801873
worker 2  receive  main      1627390801122
worker 2  post     main      1627390801875
main      receive  worker 2  1627390801875 ✅