Reputation: 43863
In javascript, I have two web workers.
var worker1 = new Worker("file.js"),
worker2 = new Worker("file.js");
This code is in file.js:
function A() {
requestAnimationFrame(A);
postMessage(123);
}
A();
This means that I have two web workers that are created at the same time and send messages to parent at the same time.
But how can I achieve something like this?
function GotWorkerMessages(a, b) {
// a is event from worker1
// b is event from worker2
}
Basically I want to run this function each time both workers respond. In case one finishes later then the other, it should not skip any data. It should still wait until both respond so each (a,b) pair is paired at the same postMessage
call.
Upvotes: 2
Views: 249
Reputation: 11
Tomas answers your problem, if you are into rxjs you can look into forkjoin(https://www.learnrxjs.io/operators/combination/forkjoin.html) too
Upvotes: 0
Reputation: 53193
I'd try this. Make a promise that will resolve when message is returned from each worker. Wait for all the promises on each worker. After you have them return them. Do not forget to handle errors, otherwise your application might act strangely.
/**
* @description Awaits for a worker to send a message and resolves with that message. Rejects on worker error.
* @param {Worker} worker
* @returns {Promise<MessageEvent>}
*/
function workerMessagePromise(worker) {
return new Promise(function (resolve, reject) {
// if this gets called, message was received OK
function messageReceived(messageEvent) {
// remove the error listener here
worker.removeEventListener("error", workerError);
resolve(messageEvent);
}
// If this gets called, there was an error in the worker
function workerError(error) {
worker.removeEventListener("message", messageReceived);
reject(error);
}
// we use "once" here, we only want one message
worker.addEventListener("error", workerError, { once: true });
worker.addEventListener("message", messageReceived, { once: true });
});
}
/**
* Requires all workers to provide one message, only returns after that
* @param {...Worker} workers
*/
async function AllWorkerMessages(...workers) {
return Promise.all([...workers].map(w => workerMessagePromise(w)));
}
Usage would be something like:
/**
* Loops for worker messages as long as enabled is true and until a worker throws an error
* @param {{enabled:boolean}} enabledObj
*/
async function loopForWorkerMessages(enabledObj, worker1, worker2) {
while (enabledObj.enabled) {
const messages = await allWorkerMessages(worker1, worker2);
GotWorkerMessages(messages[0], messages[1]);
// Or if you want to be more obscure you can write:
// GotWorkerMessages(...messages);
}
}
const worker1 = new Worker("file.js");
const worker2 = new Worker("file.js");
const enabledObj = {enabled: true};
const loopPromise = loopForWorkerMessages(enabledObj, worker1, worker2);
// later, you can stop the loop and it wont listen to messages and resolve with undefined
setTimeout(()=>enabledObj.enabled=false, 5000);
Alternative, event-based approach would be to have an aggregate object such as:
const messages = {worker1msg: null, worker2msg: null};
When you receive message from worker, fill it in the object. When both messages are set, execute your function and set them back to null
.
Upvotes: 1