Tìm hiểu Web Worker và Service Worker khác nhau thế nào, khi nào dùng mỗi loại để tăng tốc trang, chạy tác vụ nền, cache và hỗ trợ offline.

Trình duyệt chạy phần lớn JavaScript của bạn trên luồng chính—cùng nơi xử lý input người dùng, hoạt ảnh và vẽ trang. Khi có công việc nặng ở đó (phân tích dữ liệu lớn, xử lý ảnh, tính toán phức tạp), UI có thể giật hoặc “đứng yên”. Workers tồn tại để chuyển một số tác vụ ra khỏi luồng chính hoặc ra khỏi quyền điều khiển trực tiếp của trang, để app của bạn vẫn giữ được độ phản hồi.
Nếu trang của bạn đang bận thực hiện một phép tính 200ms, trình duyệt không thể cuộn mượt, phản hồi click, hoặc giữ animation ở 60fps. Workers giúp bằng cách cho phép bạn làm công việc nền trong khi luồng chính tập trung vào giao diện.
Một Web Worker là một thread JavaScript nền mà bạn tạo từ một trang. Nó phù hợp cho các tác vụ nặng CPU mà nếu chạy trên luồng chính sẽ chặn UI.
Một Service Worker là một loại worker đặc biệt nằm giữa web app và mạng. Nó có thể chặn request, cache response, và cho phép các tính năng như hỗ trợ offline và thông báo đẩy.
Hãy nghĩ Web Worker như một trợ lý làm phép tính ở phòng khác. Bạn gửi tin nhắn, nó làm việc, rồi trả kết quả.
Hãy nghĩ Service Worker như một bảo vệ ở cửa chính. Các request cho trang, script, và API đi qua nó, và nó có thể quyết định lấy từ mạng, phục vụ từ cache, hoặc trả về một phản hồi tùy chỉnh.
Cuối bài, bạn sẽ biết:
postMessage) phù hợp vào mô hình worker, và tại sao Cache Storage API quan trọng cho offlinePhần tổng quan này đặt ra “tại sao” và mô hình tư duy—tiếp theo chúng ta sẽ đi sâu vào cách từng loại worker hoạt động và vị trí của chúng trong dự án thực tế.
Khi bạn mở một trang web, hầu hết những gì bạn “cảm nhận” xảy ra trên luồng chính. Nó chịu trách nhiệm vẽ pixel (rendering), phản ứng với thao tác chạm và click (input), và chạy nhiều JavaScript.
Bởi vì rendering, xử lý input và JavaScript thường luân phiên trên cùng một luồng, một tác vụ chậm có thể khiến mọi thứ khác phải chờ. Đó là lý do các vấn đề hiệu năng thường biểu hiện bằng vấn đề phản hồi, không chỉ “mã chạy chậm”.
Cảm giác khi bị “block” đối với người dùng:
JavaScript có nhiều API bất đồng bộ—fetch(), timers, events—giúp bạn tránh chờ vô ích. Nhưng async không khiến công việc nặng tự nhiên chạy cùng lúc với rendering.
Nếu bạn thực hiện tính toán tốn kém (xử lý ảnh, phân tích JSON lớn, crypto, lọc phức tạp) trên luồng chính, nó vẫn cạnh tranh với cập nhật UI. “Async” có thể trì hoãn khi nó chạy, nhưng có thể vẫn chạy trên cùng luồng chính và vẫn gây jank khi thực sự thực thi.
Workers tồn tại để trình duyệt giữ trang phản hồi trong khi vẫn làm công việc có ý nghĩa.
Tóm lại: workers là cách bảo vệ luồng chính để app của bạn giữ tương tác trong khi thực hiện công việc nền thực thụ.
Một Web Worker là cách để chạy JavaScript ra khỏi luồng chính. Thay vì cạnh tranh với công việc UI (rendering, cuộn, phản hồi click), một worker chạy trên thread nền riêng để các tác vụ nặng có thể hoàn thành mà không làm trang cảm thấy “kẹt”.
Hãy coi nó như: trang giữ nhiệm vụ tương tác người dùng, trong khi worker xử lý công việc nặng về CPU như phân tích file lớn, tính toán số liệu, hoặc chuẩn bị dữ liệu cho biểu đồ.
Web Worker chạy trong một thread riêng với global scope riêng. Nó vẫn có quyền truy cập vào nhiều web API (timers, fetch trên nhiều trình duyệt, crypto, v.v.), nhưng được tách biệt chủ ý khỏi trang.
Có vài biến thể phổ biến:
Nếu bạn chưa từng dùng workers, hầu hết ví dụ bạn thấy sẽ là dedicated workers.
Workers không gọi trực tiếp hàm trong trang. Thay vào đó, giao tiếp diễn ra bằng gửi tin nhắn:
postMessage().\n- Worker trả lời cũng bằng postMessage().\n- Dữ liệu được truyền theo structured clone algorithm, hỗ trợ nhiều kiểu (objects, arrays, strings, numbers, Maps/Sets, ArrayBuffers, v.v.).Với dữ liệu nhị phân lớn, bạn có thể cải thiện hiệu năng bằng cách chuyển quyền sở hữu một ArrayBuffer (để nó không bị sao chép), giúp truyền tin vẫn nhanh.
Do worker bị cô lập, có vài ràng buộc chính:
window hay document. Workers chạy dưới self (worker global scope), và các API có mặt có thể khác so với luồng chính.\n- Tư duy bất đồng bộ: vì mọi thứ dựa trên message, bạn cấu trúc mã theo kiểu gửi công việc vào và nhận kết quả trả về.Dùng đúng cách, Web Worker là một trong những cách đơn giản nhất để cải thiện hiệu năng luồng chính mà không thay đổi hành vi app—chỉ là thay đổi chỗ thực hiện công việc nặng.
Web Workers phù hợp khi trang cảm thấy “kẹt” vì JavaScript đang làm quá nhiều việc trên luồng chính. Luồng chính cũng chịu trách nhiệm tương tác người dùng và rendering, nên các tác vụ nặng ở đó có thể gây jank, click trễ và cuộn bị đóng băng.
Dùng Web Worker khi bạn có công việc nặng CPU và không cần truy cập DOM:
Ví dụ thực tế: nếu bạn nhận một payload JSON lớn và việc parse khiến UI giật, hãy chuyển việc parse vào worker rồi gửi kết quả về.
Giao tiếp với worker qua postMessage. Với dữ liệu nhị phân lớn, ưu tiên transferable objects (như ArrayBuffer) để trình duyệt có thể chuyển quyền sở hữu bộ nhớ cho worker thay vì sao chép:
// main thread
worker.postMessage(buffer, [buffer]); // transfers the ArrayBuffer
Điều này đặc biệt hữu ích cho audio buffers, bytes ảnh, hoặc khối dữ liệu lớn khác.
Workers có chi phí: file bổ sung, truyền thông tin qua message, và luồng debug khác. Không dùng khi:
postMessage liên tục có thể xóa lợi ích.Nếu một tác vụ có thể gây pause có thể nhận ra (thường ~50ms+) và có thể biểu diễn như “input → compute → output” mà không cần DOM, Web Worker thường đáng dùng. Nếu chủ yếu là cập nhật UI, giữ trên luồng chính và tối ưu ở đó.
Một Service Worker là một file JavaScript đặc biệt chạy trong nền của trình duyệt và hoạt động như một lớp mạng có thể lập trình cho site của bạn. Thay vì chạy trong trang, nó nằm giữa web app và mạng, cho phép bạn quyết định gì xảy ra khi app yêu cầu tài nguyên (HTML, CSS, API, ảnh).
Service Worker có vòng đời tách biệt khỏi bất kỳ tab nào:
Bởi vì nó có thể bị dừng và khởi động lại bất kỳ lúc nào, hãy coi nó như một script sự kiện: làm việc nhanh, lưu trạng thái vào bộ nhớ bền, và tránh giả định rằng nó luôn chạy.
Service Workers bị giới hạn cùng origin (cùng domain/protocol/port) và chỉ điều khiển các trang trong scope của nó—thường là thư mục nơi file worker được phục vụ (và các đường dẫn con). Chúng cũng yêu cầu HTTPS (trừ localhost) vì chúng có thể ảnh hưởng tới request mạng.
Service Worker chủ yếu dùng để đứng giữa app và mạng. Nó có thể quyết định khi nào dùng mạng, khi nào dùng dữ liệu cache, và thực hiện chút công việc nền—không chặn trang.
Nhiệm vụ phổ biến nhất là hỗ trợ offline hoặc trải nghiệm khi kết nối yếu bằng cách cache tài nguyên và response.
Một vài chiến lược caching thực tế:
Điều này thường được triển khai bằng Cache Storage API và xử lý sự kiện fetch.
Service Workers cải thiện cảm nhận tốc độ khi quay lại bằng cách:
Kết quả là ít request mạng hơn, khởi động nhanh hơn và hiệu năng ổn định trên kết nối yếu.
Service Workers có thể cung cấp tính năng nền như push notifications và background sync (sự hỗ trợ khác nhau tùy trình duyệt). Điều đó có nghĩa bạn có thể thông báo người dùng hoặc thử lại request thất bại sau—even nếu trang không mở.
Nếu bạn xây progressive web app, Service Workers là phần cốt lõi cho:
Nếu chỉ nhớ một điều: Web Workers giúp trang làm việc nặng mà không làm đóng băng UI, trong khi Service Workers giúp app kiểm soát request mạng và hoạt động như một app có thể cài đặt (PWA).
Web Worker dành cho tác vụ nặng CPU—phân tích dữ liệu lớn, tạo thumbnail, tính toán—để luồng chính vẫn phản hồi.
Service Worker dành cho xử lý request và nhiệm vụ vòng đời app—hỗ trợ offline, chiến lược cache, background sync, push notifications. Nó có thể đứng giữa app và mạng.
Web Worker thường liên kết với một trang/tab. Khi trang đóng, worker thường kết thúc (trừ các trường hợp đặc biệt như SharedWorker).
Service Worker là event-driven. Trình duyệt có thể khởi động nó để xử lý một sự kiện (ví dụ fetch hoặc push), rồi dừng khi rảnh. Điều đó có nghĩa nó có thể chạy ngay cả khi không có tab nào mở, miễn là có sự kiện kích hoạt.
Web Worker không thể chặn request mà trang thực hiện. Nó có thể fetch() dữ liệu, nhưng không thể ghi đè, cache, hoặc phục vụ phản hồi cho phần khác của site.
Service Worker có thể chặn request mạng (qua sự kiện fetch), quyết định đi ra mạng, trả từ cache, hoặc trả fallback.
Web Worker không quản lý cache HTTP cho app của bạn.
Service Worker thường dùng Cache Storage API để lưu và phục vụ cặp request/response—đây là nền tảng cho caching offline và tải lại nhanh.
Chạy một worker chủ yếu liên quan đến nó chạy ở đâu và nó được tải như thế nào. Web Workers được tạo trực tiếp bởi script trang. Service Workers được trình duyệt cài đặt và đứng “trước” các request mạng của site.
Web Worker được khởi tạo khi trang tạo một worker. Bạn chỉ định tới một file JS riêng, rồi giao tiếp qua postMessage.
// main.js (chạy trên trang)
const worker = new Worker('/workers/resize-worker.js', { type: 'module' });
worker.postMessage({ action: 'start', payload: { /* ... */ } });
worker.onmessage = (event) => {
console.log('From worker:', event.data);
};
Mô hình tư duy tốt: file worker chỉ là một URL script khác mà trang có thể fetch, nhưng nó chạy ngoài luồng chính.
Service Workers phải được đăng ký từ một trang mà người dùng truy cập:
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
Sau khi đăng ký, trình duyệt xử lý vòng đời install/activate. sw.js có thể lắng nghe event như install, activate, và fetch.
Service Workers có thể chặn request mạng và cache response. Nếu đăng ký được cho phép qua HTTP, kẻ tấn công có thể thay thế sw.js độc hại và kiểm soát các lần truy cập sau. HTTPS (hoặc http://localhost khi dev) bảo vệ script và traffic mà nó có thể ảnh hưởng.
Trình duyệt cache và cập nhật workers khác với script trang bình thường. Lên kế hoạch cập nhật:
sw.js/bundle worker mới).\n- Với Service Workers, mong có flow “update”: worker mới cài rồi activate khi an toàn.\n- Khi thay đổi quy tắc caching, thêm logic cleanup trong activation để cache cũ không tồn đọng.Nếu bạn muốn rollout mượt hơn, xem /blog/debugging-workers cho thói quen test bắt sớm các edge case cập nhật.
Workers lỗi theo cách khác với JavaScript trang: chúng chạy trong context riêng, có console riêng, và có thể bị trình duyệt khởi động lại. Một quy trình debug tốt sẽ cứu bạn hàng giờ làm việc.
Mở DevTools và tìm target worker. Trong Chrome/Edge, bạn thường thấy workers liệt kê dưới Sources (hoặc qua “Dedicated worker”) và trong bộ chọn context của Console.
Dùng cùng công cụ như trên luồng chính:
onmessage và các hàm chạy dài.\n- Profiling hiệu năng: ghi trace Performance và xác nhận luồng chính vẫn phản hồi trong khi worker làm việc.Nếu tin nhắn có vẻ “mất”, kiểm tra cả hai phía: xác minh bạn gọi worker.postMessage(...), worker có self.onmessage = ..., và cấu trúc tin nhắn khớp.
Service Workers debug tốt nhất trong Application panel:
Cũng xem Console cho lỗi install/activate/fetch—chúng thường giải thích tại sao caching hoặc offline không hoạt động.
Vấn đề cache là nguyên nhân tốn thời gian nhất: cache sai file (hoặc quá mạnh tay) có thể giữ HTML/JS cũ. Khi test, thử hard reload và xác nhận điều gì thực sự được phục vụ từ cache.
Để test thực tế, dùng DevTools để:
Nếu bạn lặp nhanh trên một PWA, tạo một app baseline sạch (với Service Worker và output build dự đoán) rồi tinh chỉnh chiến lược cache từ đó sẽ giúp. Platforms like Koder.ai có thể hữu ích cho việc này: bạn có thể prototype một React-based web app từ prompt chat, xuất source, rồi tinh chỉnh worker và quy tắc cache với vòng phản hồi chặt hơn.
Workers có thể làm app mượt và mạnh hơn, nhưng chúng cũng thay đổi nơi mã chạy và những gì nó có thể truy cập. Một kiểm tra nhanh về bảo mật, quyền riêng tư và hiệu năng sẽ tránh bug bất ngờ—và người dùng khó chịu.
Cả Web Workers và Service Workers bị giới hạn bởi same-origin policy: chúng chỉ tương tác trực tiếp với tài nguyên cùng scheme/host/port (trừ khi server cho phép cross-origin qua CORS). Điều này ngăn worker lặng lẽ lấy dữ liệu từ site khác rồi trộn vào app của bạn.
Service Workers có thêm biện pháp bảo vệ: thường yêu cầu HTTPS (hoặc localhost khi dev) vì chúng có thể chặn request mạng. Đối xử chúng như mã có đặc quyền: giữ dependencies tối thiểu, tránh tải mã động, và version logic caching cẩn thận để cache cũ không tiếp tục phục vụ file lỗi thời.
Các tính năng nền nên cảm thấy dễ đoán. Push notifications rất mạnh, nhưng hộp thoại cấp phép dễ bị lạm dụng.
Hãy hỏi phép chỉ khi có lợi rõ ràng (ví dụ sau khi người dùng bật cảnh báo trong cài đặt), và giải thích họ sẽ nhận gì. Nếu bạn sync hoặc prefetch dữ liệu nền, nói rõ bằng ngôn ngữ đơn giản—người dùng để ý hoạt động mạng bất ngờ hoặc thông báo.
Workers không miễn phí về hiệu năng. Dùng quá tay có thể phản tác dụng:
postMessage thường xuyên (đặc biệt với object lớn) có thể là cổ chai. Ưu tiên gom lô và dùng transferables khi phù hợp.\n- Chi phí bộ nhớ: mỗi worker có bộ nhớ và chi phí khởi tạo; quá nhiều worker tăng RAM và tiêu pin.\n- Tăng trưởng cache: Service Worker cache quá mạnh có thể làm đầy bộ nhớ. Thêm giới hạn cache và cleanup khi cập nhật.Không mọi trình duyệt đều hỗ trợ mọi khả năng (hoặc người dùng chặn quyền). Kiểm tra tính năng và degrade mượt:
if ('serviceWorker' in navigator) {
// register service worker
} else {
// continue without offline features
}
Mục tiêu: chức năng cốt lõi vẫn hoạt động, với các “nice-to-have” (offline, push, tính toán nặng) thêm vào khi có sẵn.
Web Workers và Service Workers giải quyết các vấn đề khác nhau, nên chúng kết hợp tốt khi app cần cả tính toán nặng và tải nhanh, tin cậy. Mô hình tư duy: Web Worker = compute, Service Worker = mạng + caching, luồng chính = UI.
Giả sử app cho phép người dùng chỉnh sửa ảnh (resize, filter, xóa nền) và xem gallery sau khi offline.
Cách tiếp cận “compute rồi cache” giữ trách nhiệm rõ ràng: worker tạo ra đầu ra, service worker quyết định lưu và phục vụ.
Với app có feed, form, hoặc dữ liệu trường:
Ngay cả khi không có full background sync, service worker vẫn cải thiện cảm nhận bằng cách phục vụ response cache trong khi app cập nhật nền.
Tránh trộn vai trò:
postMessage).\n- Service Worker: định tuyến request, chiến lược caching, fallback offline.Không. Service Worker chạy trong nền, tách biệt khỏi bất kỳ tab trang nào, và không có quyền truy cập DOM (các phần tử HTML của trang).
Sự tách biệt này có chủ ý: Service Workers được thiết kế để vẫn hoạt động ngay cả khi không có trang mở (ví dụ phản hồi một sự kiện push hoặc phục vụ file cache). Vì có thể không có document đang hoạt động để thao tác, trình duyệt giữ nó cô lập.
Nếu Service Worker cần ảnh hưởng tới giao diện người dùng, nó giao tiếp với trang qua messaging (ví dụ postMessage) để trang cập nhật UI.
Không. Web Workers và Service Workers độc lập.
Bạn có thể dùng riêng từng loại, hoặc cả hai nếu app cần cả tính toán nền và tính năng mạng/offline.
Trong các trình duyệt hiện đại, Web Workers được hỗ trợ rộng rãi và thường là lựa chọn nền tảng an toàn hơn.
Service Workers cũng được hỗ trợ rộng rãi trên các trình duyệt chính hiện nay, nhưng có thêm yêu cầu và trường hợp biên:
localhost khi phát triển).\n- Một số tính năng (như push notifications) khác nhau theo trình duyệt và nền tảng.Nếu cần tương thích rộng, coi Service Worker như nâng cấp tiến bộ: xây trải nghiệm lõi tốt trước, rồi thêm offline/push khi có thể.
Không tự động.
Lợi ích thực sự đến từ dùng đúng worker cho nút cổ chai đúng, và đo lường trước/sau.
Sử dụng Web Worker khi bạn có công việc nặng về CPU có thể diễn đạt dưới dạng input → compute → output và không cần DOM.
Những trường hợp phù hợp: phân tích/biến đổi payload lớn, nén/giải nén, crypto, xử lý ảnh/âm thanh, và lọc/so sánh phức tạp. Nếu công việc chủ yếu là cập nhật UI hoặc đọc/ghi DOM thường xuyên thì worker không hữu ích (và worker cũng không thể truy cập DOM).
Dùng Service Worker khi bạn cần kiểm soát mạng: hỗ trợ offline, chiến lược cache, tăng tốc truy cập lặp lại, định tuyến request, và (nếu trình duyệt hỗ trợ) push/background sync.
Nếu vấn đề là “UI bị đóng băng khi tính toán”, đó là vấn đề của Web Worker. Nếu vấn đề là “tải chậm/không hoạt động khi offline”, đó là vấn đề của Service Worker.
Không. Web Workers và Service Workers là hai tính năng độc lập.
Bạn có thể dùng riêng từng loại, hoặc kết hợp chúng khi cần cả compute và tính năng offline/mạng.
Chủ yếu là phạm vi và vòng đời.
fetch) ngay cả khi không có trang mở, rồi tắt khi rảnh.Không. Web Workers không có window/document.
Nếu bạn cần ảnh hưởng UI, gửi dữ liệu trở lại luồng chính bằng postMessage() rồi cập nhật DOM trong mã trang. Giữ worker chỉ làm nhiệm vụ tính toán thuần túy.
Không. Service Workers không có quyền truy cập DOM.
Để tác động tới những gì người dùng nhìn thấy, hãy giao tiếp với các trang đang được điều khiển bằng messaging (ví dụ dùng Clients API + postMessage()), rồi để trang cập nhật UI.
Dùng postMessage() ở cả hai phía.
worker.postMessage(data)\n- Worker → luồng chính: self.postMessage(result)\n
Với dữ liệu nhị phân lớn, ưu tiên transferables (như ArrayBuffer) để tránh sao chép:Service Workers ngồi giữa app và mạng và có thể phản hồi request bằng Cache Storage API.
Các chiến lược phổ biến:
Hãy chọn chiến lược theo loại tài nguyên (app shell vs dữ liệu API), không áp một quy tắc duy nhất cho tất cả.
Có, nhưng giữ vai trò rõ ràng.
Một mẫu thường thấy:
Tránh trộn giao nhiệm vụ giữa các bối cảnh để hiệu năng dễ dự đoán.
Dùng đúng bề mặt DevTools cho từng loại.
onmessage, và profile để chắc luồng chính vẫn phản hồi.\n- Service Worker: dùng bảng Application để kiểm tra registration/scope, bật “Update on reload”, và reset trạng thái bằng “Unregister” hoặc “Skip waiting”.Khi debug lỗi cache, luôn xác định rõ thứ gì đang được phục vụ (network hay cache) và thử chế độ offline/throttling.
worker.postMessage(buffer, [buffer]);