I'm new on workers, so base on this question I was trying the following:
// 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()
}
Expecting this:
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" }
But instead what I'm getting is:
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?
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?
I suspect that it's because you're closing each of the workers before postMessage
can complete. In your worker.ts
:
self.postMessage(data)
self.close()
From the MDN page DedicatedWorkerGlobalScope.close():
The
close()
method of theDedicatedWorkerGlobalScope
interface discards any tasks queued in theDedicatedWorkerGlobalScope
's event loop, effectively closing this particular scope.
Below, I've included a refactor of your example with a bit of added type safety and more verbose logging so you can see more events and some timing info. In the example, instead of calling self.close()
from within each worker, 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 (although they might help with type checking in your editor).
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));
}
Run example:
/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 ✅