12/08/2018, 17:35

Web Workers (part 1): Tổng quan về Web Workers

Single-threaded của JS và hạn chế của nó Như các bạn có thể đã biết, javascript là một ngôn ngữ single-threaded. Trên web browser, mỗi cửa sổ hoặc mỗi tab chỉ có 1 luồng xử lý duy nhất. Trở ngại của single-thread trên browser đó là khi code đang được thực thi, bạn sẽ ko thể làm gì khác, UI của ...

Single-threaded của JS và hạn chế của nó

Như các bạn có thể đã biết, javascript là một ngôn ngữ single-threaded. Trên web browser, mỗi cửa sổ hoặc mỗi tab chỉ có 1 luồng xử lý duy nhất. Trở ngại của single-thread trên browser đó là khi code đang được thực thi, bạn sẽ ko thể làm gì khác, UI của bạn sẽ bị khoá cứng. Có một giải pháp quen thuộc để giải quyết vấn đề này đó Event Loop, asynchronous event. Quen thuộc nhất đó AJAX. Tuy vậy, AJAX cũng vẫn chỉ giải quyết đuợc một phần vấn đề. Ví dụ khi các bạn sử dụng AJAX, cụ thể, các bạn thực hiện một GET request lên server, các bạn bind một callback function xử lý dữ liệu cho event khi có response trả về, nói nôm na theo ngôn ngữ của con người là "ê, khi nào dữ liệu về thì xử lý như thế này nhé". Chúng ta rung đùi làm việc khác, kệ cho response về lúc nào thì về. Nếu dữ liệu nhỏ, callback function của bạn được xử lý cái roẹt. Tuy nhiên nếu dữ liệu lớn, lúc này nhược điểm sẽ lộ ra, UI của bạn sẽ đơ cứng, đơ cho đến khi callback function của bạn chạy xong. Vấn đề do đâu, do callback function vẫn phải chạy trên luồng duy nhất của phiên làm việc.

Web Workers save the day

Web Worker là gì?

Phiên bản HTML5 cung cấp cho chúng ta một loạt APIs rất hữu ích và một trong số chúng giúp giải quyết vấn đề nêu trên, đó là Web Worker. Web Worker cho phép xử lý script ở một luồng dưới background tách biệt với luồng chính (chủ yếu là UI), giúp UI ko bị chậm lại hoặc khoá cứng khi phải xử lý dữ liệu nặng.

Xin nhấn mạnh lại là JS vẫn đơn luồng mà thôi, còn Web Workers là một APIs được cung cấp bới trình duyệt giúp giải quyết nhược điểm đơn luồng của JS.

Các loại Worker.

Chúng ta có 3 loại Worker chính:

  1. Dedicated Worker Worker chỉ dùng được trong nội bộ main thread. Dedicated worker hiện đã được hỗ trỡ trên ~95% các trình duyệt.

  2. Shared Worker Worker có thể được truy cập bởi nhiều process khác nhau như tab khác nhau, iframe khác nhau, miễn sao chúng cùng một origin domain. Nó cũng phức tạp hơn Dedicated Worker, chúng ta cần định nghĩa cổng cho các lần giao tiếp. Shared worker mới được hỗ trợ ở mức khá hạn chế, mới chỉ khoảng 43% các trình duyệt

  3. Service Worker Hoạt động như một proxy server nằm giữa web app, trình duyệt, và network. Mục đích chính của service worker là tạo ra trải nghiệm sử dụng web app xuyên suốt ngay cả khi ko có internet (giống native app), service worker đánh chặn network, dựa vào tình trạng kết nối internet để quyết định việc sử dụng cached asset hay fetch dữ liệu tươi từ phía server giúp trải nghiệm người dùng ko bị gián đoạn. Service worker cũng cho phép thực hiện push notification và thực hiện đồng bộ APIs ngầm giúp người dùng ko thể nhận ra giai đoạn update app lên phiên bản mới. Service worker hiện được hỗ trợ ở mức ~75% các trình duyệt, chủ yếu được sử dụng nhiều nhất trên chrome và firefox.

Cách hoạt động

Mình sẽ sử dụng Dedicated Worker làm ví dụ vì cách dùng của nó đơn giản nhất. Worker nói chung và Dedicated Worker nói riêng được sử dụng thông qua javascript, được khởi tạo bằng constructor Worker(). Worker sẽ gọi vào 1 file javascript chứa đoạn code chúng ta muốn xử lý ở luồng worker, tách biệt với luồng chính do window object nắm giữ. Dữ liệu được gửi qua lại giữa main thread và worker thread qua hệ thống message sử dụng method postMessage(), và được nhận thông qua event handler onmessage ở cả 2 phía. Dữ liệu này được copy và gửi đi chứ ko dùng chung.

Sau đây là 1 ví dụ nhỏ sử dụng Dedicated Worker.

<button onclick="calculate()">calculate</button>

<script>
  function calculate() {
    worker.postMessage({'data': [1, 2, 3, 4]});
  }
  var worker = new Worker('calculate-average.js');
  worker.addEventListener('message', function(message) {
    console.log(message.data);
  }, false);
  
</script>
function calculateAverage(data) {
  // do calculating average number
}

self.addEventListener('message', function(e) {
  var data = e.data;
  var result = calculateAverage(data);
  self.postMessage(result);
}, false);

Làm được gì và không làm được gì bên trong Web Worker

Trong worker thread chúng ta có thể dùng được:

  • navigator object
  • location object (read-only)
  • XMLHttpRequest
  • setTimeout()/clearTimeout() and setInterval()/clearInterval()
  • Application Cache
  • import script bên ngoài importScripts()
  • Tạo một web worker khác

Hạn chế là chúng ta sẽ không thể truy cập đến:

  • DOM
  • window object
  • document object
  • parent object

Ý kiến cá nhân của mình thì mình cho rằng đây cũng ko hẳn là hạn chế, vì Web Worker có nhiệm vụ riêng của nó, ko cần truy cập vào DOM làm gì. Hơn nữa các bạn cũng ko cần lo lắng vì nếu các bạn cần xử lý một khối lượng DOM lớn, các bạn vẫn có thể truy cập từ main thread và gửi vào worker thread để xử lý.

Web Worker được ứng dụng trong những trường hợp nào

Tổng quan thì chúng ta nên sử dụng Web Worker cho các tác vụ tốn cpu. Cụ thể hơn:

  • Thực thi thuật toán mã hoá. Đây là một tác vụ tốn cpu, và chỉ cần nhiều đến thao tác tính toán, ko cần đến các APIs khác như DOM manipulation, rất phù hợp để dùng trong worker.
  • Prefetching data. Bạn có thể tính toán việc tải trước một lượng dữ liệu nhất định bằng worker và lưu lại cho các tác vụ trong tương lai.
  • Progressive Web App (PWA): Tạo ra trải nghiệm ko gián đoạn cho web app giống như native app
  • Các thao tác xử lý text thời gian thực như code highlight, spell checking ...
  • ...

Tạm kết

Trong các bài viết tiếp theo, mình sẽ giới thiệu chi tiết hơn về từng lại Worker và cách ứng dụng của nó.

Tham khảo

  • https://caniuse.com
  • https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
0