Frontend Development 14 min read

Using Web Workers for Large Excel Export and Batch Image Compression in Frontend Projects

This article explains how to leverage Web Workers and OffscreenCanvas to offload heavy Excel file generation and bulk image compression from the main thread, improving UI responsiveness and reducing rendering stalls in typical backend‑admin web applications.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Using Web Workers for Large Excel Export and Batch Image Compression in Frontend Projects

Preface

The author introduces the motivation: many developers learn Web Workers but struggle to find practical scenarios. Two simple, project‑relevant examples—large Excel export and batch image compression—are presented.

Browser Processes and Threads

The article lists the main browser processes (browser main process, GPU process, third‑party process, render process) and the key threads inside a render process (GUI rendering thread, JS engine thread, event thread, timer thread, async HTTP thread, worker thread). It explains that the GUI thread and JS engine thread share the render process and are mutually exclusive, causing UI freezes when the JS thread is busy.

Excel Large‑File Export

Two export strategies are compared:

Main‑thread export : Uses exceljs to build a workbook, writes it to a Blob , and saves it with file‑save .

Worker‑thread export : Creates an ExcelWorker , posts column and data information, builds the workbook inside the worker, converts it to a Blob , and sends the result back to the main thread for saving.

Both implementations are shown in the following code snippets:

// index.tsx
import { Button, Table } from 'antd';
import React, { useState, useEffect } from 'react';
import ExcelJS from 'exceljs';
import FileSaver from 'file-saver';
import ExcelWorker from './excel.worker?worker';

// ... data generation omitted for brevity ...

const mainExportExcel = () => {
  const workbook = new ExcelJS.Workbook();
  const worksheet = workbook.addWorksheet('sheet1');
  // set title and columns, add rows
  workbook.xlsx.writeBuffer().then(buffer => {
    const file = new Blob([buffer], { type: 'application/octet-stream' });
    FileSaver.saveAs(file, 'ExcelJS.xlsx');
  });
};

const workerExportExcel = async () => {
  const file = await new Promise(resolve => {
    const myWorker = new ExcelWorker();
    myWorker.postMessage({ columns, dataSource });
    myWorker.onmessage = e => { resolve(e.data.data); myWorker.terminate(); };
  });
  FileSaver.saveAs(file, 'ExcelJS.xlsx');
};
// excel.worker.ts
import ExcelJS from 'exceljs';

onmessage = function (e) {
  const { columns, dataSource } = e.data;
  const workbook = new ExcelJS.Workbook();
  const worksheet = workbook.addWorksheet('sheet1');
  // set title and columns, add rows
  workbook.xlsx.writeBuffer().then(buffer => {
    const file = new Blob([buffer], { type: 'application/octet-stream' });
    self.postMessage({ data: file, name: 'worker test' });
    self.close();
  });
};

Performance tests show that the main‑thread export of 30 000 rows causes noticeable UI stutter, while the worker‑based export remains smooth.

OffscreenCanvas

OffscreenCanvas is introduced as an experimental feature that can be used inside Web Workers to perform canvas drawing without blocking the main thread. Compatibility is good for Chrome, Edge, and Firefox, but not for Safari or IE.

Batch Image Compression

The article compares two approaches for compressing many images before upload:

Main‑thread compression : Loads each image, draws it onto a regular canvas, and calls canvas.toDataURL('image/jpeg', 0.75) to obtain a compressed base64 string.

Worker‑thread compression : Fetches images as Blob s, creates multiple workers, each uses an OffscreenCanvas to draw the image, calls convertToBlob for JPEG compression, converts the blob to a data URL via FileReader , and sends the result back.

Key code snippets:

// index.tsx (main compression)
const compressImg = img => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = img.width;
  canvas.height = img.height;
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  return canvas.toDataURL('image/jpeg', 0.75);
};
// compress.worker.ts (worker compression)
onmessage = async function (e) {
  const { imageList } = e.data;
  const resList = [];
  for (let img of imageList) {
    const offscreen = new OffscreenCanvas(100, 100);
    const ctx = offscreen.getContext('2d');
    const imgData = await createImageBitmap(img);
    offscreen.width = imgData.width;
    offscreen.height = imgData.height;
    ctx.drawImage(imgData, 0, 0, offscreen.width, offscreen.height);
    const blob = await offscreen.convertToBlob({ type: 'image/jpeg', quality: 0.75 });
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    const dataUrl = await new Promise(resolve => { reader.onloadend = () => resolve(reader.result); });
    resList.push(dataUrl);
  }
  self.postMessage({ data: resList, name: 'worker test' });
  self.close();
};

Benchmark results show that compressing 100 images 100 times on the main thread takes about 108 seconds and blocks the GUI, while using five workers reduces the total time to roughly 37 seconds and keeps the UI responsive.

Conclusion

The author summarizes that Web Workers provide practical solutions for two common pain points—large Excel export and bulk image compression—especially in backend‑admin systems lacking obvious features. By moving heavy computation to workers and optionally using OffscreenCanvas, developers can avoid UI freezes and improve overall performance.

image compressionOffscreenCanvasweb workersExcel Exportfrontend performance
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.