RxJS

RxJS là một thư viện dùng cho việc phát triển web được dùng rất rộng rãi hiện nay. Thư viện này chuyên trị các vấn đề liên quan đến sự kiện (events), hơn nữa còn bao gồm các framework, thư viện và tiện ích phong phú nên trở nên rất hấp dẫn với cộng đồng dev. Ngoài ra dùng RxJS cũng giúp chúng ta hiểu rõ hơn về Reactive Programming, một phong cách lập trình rất khác.

Tuy vậy, học RxJS không hề dễ vì có rất nhiều khái niệm, các tầng API và đặc biệt là khác biệt về tư duy lập trình. Bài viết này sẽ giới thiệu qua về RxJS để các bạn dễ dàng tiếp cận và sau đó đào sâu hơn nữa vào thư viện rất tuyệt vời này.

Đặc điểm cốt yếu của RxJS đó là nó xử lý các sự kiện và luồng dữ liệu bất đồng bộ nhờ vào observable sequence – các chuỗi sự kiện quan sát được. Cốt lõi của nó là Observable type, và nó có các type liên quan (Observer, Schedulers, Subjects). Các toán tử mà thư viện này dùng lấy cảm hứng từ các toán tử của Array như map, filter, reduce, every… chỉ khác là thay vì xử lý các dữ liệu tĩnh của Array, RxJS dùng các toán tử này cho các luồng dữ liệu.

Để dễ hình dung, hãy cùng xem 1 ví dụ đơn giản sau

Thông thường bạn sẽ gán một sự kiện vào html như thế nào? Đúng rồi, bạn cần dùng hàm addEventListener()

document.addEventListener('click', () => console.log('Clicked!'));

Nếu dùng RxJS

import { fromEvent } from 'rxjs';

fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));

Ở đây hàm fromEvent() có vai trò như hàm addEventListener ở trên, nhưng thay vì tạo ra một Event thì nó tạo ra một observable, và bạn có thể subscribe nó giống như các data stream thông thường.

Nếu bạn đã biết tới khái niệm pure function (hàm thuần khiết), tức là các hàm chỉ thay đổi các giá trị từ input để return giá trị mới mà không gây ra các side effects (ảnh hưởng tới các biến bên ngoài hàm) thì bạn sẽ thấy được ưu điểm của việc sử dụng pure function. RxJS tận dụng mạnh mẽ các pure function này, chẳng hạn qua ví dụ sau

Với lập trình JS thông thường, khi muốn đếm số click chuột thì ta làm thế này

let count = 0;
document.addEventListener('click', () => console.log(`Clicked ${++count} times`));

Còn với RxJS

import { fromEvent } from 'rxjs';
import { scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(scan(count => count + 1, 0))
  .subscribe(count => console.log(`Clicked ${count} times`));

Có thể bạn hơi bối rối khi chưa làm quen với các toán tử của RxJS. Ở ví dụ trên pipe() là hàm dùng để tương tác với giá trị trả về từ observable, trong pipe bạn có thể dùng các hàm toán tử khác nhau để thao tác với giá trị đó. Ở đây scan() hoạt động tương tự với hàm reduce() của Array, nó giúp thực hiện phép toán cộng dồn giá trị count ở mỗi lần có giá trị trả về. Như vậy khi dùng RxJS thì bạn không cần khai báo 1 biến count và tương tác trực tiếp với nó mà vẫn có thể đếm được click thông qua giá trị count trả về từ observable.

RxJS có rất nhiều toán tử dùng để tương tác với các event qua các observable. Dưới đây là một ví dụ về cách để hạn chế số click trong một giây

JavaScipt thuần

let count = 0;
let rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', () => {
  if (Date.now() - lastClick >= rate) {
    console.log(`Clicked ${++count} times`);
    lastClick = Date.now();
  }
});

Với RxJS

import { fromEvent } from 'rxjs';
import { throttleTime, scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(
    throttleTime(1000),
    scan(count => count + 1, 0)
  )
  .subscribe(count => console.log(`Clicked ${count} times`));

Có thể thấy rõ ràng rằng RxJS giải quyết vấn đề quản lý flow rất gọn gàng so với JS thuần. Ngoài hàm throttleTime() ở trên còn có nhiều hàm khác như filter,  delay,  debounceTime,  take,  takeUntildistinct,  distinctUntilChanged

Hãy cùng xem một ví dụ cuối cùng về xử lý giá trị của RxJS.

Ví dụ dưới đây là về bài toán cộng giá trị tọa độ x với mỗi click. với JavaScipt thuần thì sẽ như sau

let count = 0;
const rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', event => {
  if (Date.now() - lastClick >= rate) {
    count += event.clientX;
    console.log(count);
    lastClick = Date.now();
  }
});

Xử lý với RxJS

import { fromEvent } from 'rxjs';
import { throttleTime, map, scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(
    throttleTime(1000),
    map(event => event.clientX),
    scan((count, clientX) => count + clientX, 0)
  )
  .subscribe(count => console.log(count));

Trong ví dụ này thao tác click sẽ được chuyển thành một observable, set thời gian chờ là 1 giây, sau đó mỗi event click chuột được map sang giá trị clientX của nó, cuối cùng dùng hàm scan để cộng dồn giá trị clientX.

2 thoughts on “RxJS

Leave a comment