- Published on
Optimize parallel processing in React application
- Authors
- Name
- Khánh
Concurrency patterns for Efficient Task Management
In this article, we will explore two concurrency patterns that can help you optimize parallel processing in your React application: Scatter-Gather and Work-stealing.
Scatter-Gather Pattern
Imagine that you have a large dataset distributed across multiple servers or databases. The scatter-gather pattern empowers you to process this data in parallel across multiple web workers.
Scatter phase
Divide the data into smaller chunks. Each chunk is processed in different web worker.
Gather phase
Each processed chunk is sent back to the main thread. The main thread gather and combine the results.
Example
const data = splitLargeData(dataFromServer); // split data into chunks
function scatterGather(workerCount) {
const results = [];
const workers = [];
for (let i = 0; i < workerCount; i++) {
const worker = new Worker('scatterGatherWorker.js');
workers.push(worker);
}
// thinking that the workers are the load balancer (workers will work frequency) to make sure the workers are not overloaded
const promises = data.map((chunk) => {
const worker = workers.shift();
return new Promise((resolve, reject) => {
worker.postMessage(chunk);
worker.onmessage = (event) => {
resolve(event.data);
workers.push(worker); // add worker back to the pool
}
worker.postMessage({ type: 'process', data: chunk });
})
})
return Promise.all(promises).then(processedChunks => {
results.push(...processedChunks);
return combineResults(results);
});
}
async function handleClick(){
try {
const finalREsult = await scatterGather(navigator.hardwareConcurrency);
console.log("Final processed data: ", finalResult);
} catch (error) {
console.error("Error: ", error);
}
}
Work-stealing Pattern
Thinking that your workers working in a complexities task and some of them finished their task early. The work-stealing pattern allows the workers to steal tasks from other workers.
Example
const taskQueue = new SharedArrayBuffer(/* size */);
const workers = [];
// Create workers
for (let i = 0; i < workerCount; i++) {
const worker = new Worker('workStealingWorker.js');
workers.push(worker);
}
// In each worker
self.onmessage = async (e) => {
const { task } = e.data;
// Process the task
const result = await processTask(task);
// If worker is idle, try to steal work
if (isIdle) {
const stolenTask = await tryStealWork();
if (stolenTask) {
// Process stolen task
processTask(stolenTask);
}
}
// Send result back
self.postMessage({ result });
};
Conclusion
The scatter-gather pattern is useful when you have a large amount of data to process and you want to distribute the work across multiple workers. The work-stealing pattern is useful when you have a large amount of data to process and you want to distribute the work across multiple workers and you want to make sure that the workers are not idle.