如何让 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 ✅
我是 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 theDedicatedWorkerGlobalScope
interface discards any tasks queued in theDedicatedWorkerGlobalScope
'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 ✅