Published on

Optimize parallel processing in React application

Authors
  • avatar
    Name
    Khánh
    Twitter

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.

References

Scatter-Gather Pattern Work-stealing Pattern