Thiếu RxJS chắc Hà Lan buồn lắm

4101

Người viết: Le Thi Hao

Trailer

Ngày tháng cuối năm bận rộn, các anh chị em hăng say làm việc để đảm bảo kết quả đúng hạn đề ra. Thôi thì dành vài phút xem Trailer Mắt biếc version Lập trình viên cùng mình nhé, vừa giải trí vừa tích lũy kiến thức haaaa

Thiếu RxJS chắc Hà Lan buồn lắm

♪ ♪ ♪ có chàng trai code lên cây ~~ ♪ ♪ ♪

Ngạn dev: – Nộiiii, sau này con muốn cưới Hà Lan làm vợ !

Nội Ngạn: – Con bé ấy dễ thương. Mắt nó đẹp như Mắt biếc. Nội sợ … con bé đó sau này sẽ khổ.

 

Theo dòng thời gian, sau khi rời làng Đo ĐoHà Lan lên thành phố đã xin vào một công ty phần mềm ở mảng Front-end Web. Và đúng như lời của nội, Hà Lan khổ thiệt 😭😭. Mắt không còn biếc như xưa vì phải ngồi máy tính suy nghĩ quá nhiều 🙂))

Hà Lan: – Lập trình không đồng bộ chưa bao giờ dễ dàng cả !

Cũng chính vì vậy mà khi tiếp cận tới RxJSHà Lan cảm thấy hơi khoai mì. Chúng ta hãy cùng Hà Lan tìm hiểu xem RxJS là gì để giúp đời bớt khổ nhé 

Reactive Programming

Trước khi đi vào RxJS, ta ghé qua khái niệm Reactive Programming:

Reactive programming is programming with asynchronous data streams

Reactive programming là phương pháp lập trình xoay quanh data streams và nó xử lý các vấn đề của asynchronousĐừng hiểu lầm nhé, nó có thể xử lý với cả synchronous nữa.

Bạn có thể hình dung data streams như hình sau, với data được gửi đến trong suốt dòng thời gian của một stream (over time), giống như một array có các phần tử được gửi đến lần lượt theo thời gian:

Thiếu RxJS chắc Hà Lan buồn lắm

Và chúng ta có thể coi mọi thứ là streamsingle valuearrayevent.

Thiếu RxJS chắc Hà Lan buồn lắm

Không những thế, khi thao tác với stream, chúng ta có thể có valueerror, hay complete signals – Điều mà các API trước đây của các event trong Javascript còn thiếu, chúng có qua nhiều interface khác nhau cho các loại event khác nhau, Observable sinh ra để tổng quát hóa các interface đó lại.

RxJS

Như đã nói ở trên, RxJS giúp chúng ta có được tính chất reactive trong lập trình ứng dụng Javascript:

RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code.

Để giải thích cụ thể hơn, mình xin phép gói gọn nhỏ trong ReactJS chẳng hạn, chúng ta thường sử dụng FluxReduxMobXMST để xử lý các luồng dữ liệu. Trong Redux, ta có một số midleware để xử lý vấn đề bất đồng bộ có thể kể đến như redux-sagaredux-thunk. Ở Saga và Thunk thì đôi khi khá nhiều biến làm phức tạp reducer lên khi ta chỉ muốn thực hiện một nhiệm vụ đơn giản.

Và đây chính là lúc ta tìm tới RxJS 🎉🎉. RxJS cung cấp một số API dùng cho bất đồng bộ hiệu quả, từ xử lý luồng dữ liệu cho tới bắt lỗi và dừng request; điển hình có thể kể tới như Observables – hỗ trợ subscribe những event và thực hiện các RxJS operations tương ứng.


Có lẽ đó cũng là lý do Angular đi kèm với một dependency là RxJS giúp cho nó trở nên reactive, một ứng dụng Angular là một reactive system. Dễ thấy nhất ở đây chính là EventEmitter, hay Reactive Forms, bạn nào đã từng học Angular hẳn cũng không xa lạ gì với nó.


Hmmm…

Có vẻ như có khá nhiều thuật ngữ mới nhỉ, chúng ta sẽ làm rõ nó vào phần tiếp theo  

RxJS Concepts

Một vài khái niệm cơ bản trong RxJS:

  • Stream
  • Producer
  • Observable
  • Observer
  • Subscription
  • Operator
  • Subject

Cùng tìm hiểu chúng nào !

Stream

Định nghĩa

A stream is a sequence of data elements made available over time, as items on a conveyor belt being processed one at a time rather than in large batches.

Như vậy, theo một cách khác, cũng có thể hiểu rằng stream là một chuỗi các sự kiện đang diễn ra được sắp xếp theo thời gian.

Ví dụ

Số lần nhấp vào nút trong 1 giây. Vì vậy, tất cả các nhấp chuột sẽ được nhóm thành một luồng.

Trong RxJS, ta có thể tạo ra một stream từ một hoặc nhiều giá trị primitivearrayeventPromisecallback hay một vài kiểu cấu trúc dữ liệu khác.

Producer

Định nghĩa

A producer is the source of values for your observable

Ví dụ

Nó là bất kì kiểu dữ liệu nào đó cho phép ta lấy, truyền giá trị qua observer.next(value) như web socketDOM eventsiteratorarray.

Observable

Định nghĩa

Observable is just a function that ties an observer to a producer and returns a function.

Rx.Observable class có chức năng tạo ra các observable từ các kiểu dữ liệu đã kể phía trên.

Một điểm lưu ý ở đây, Observable is inert. Nói vui điều này có nghĩa là, Observable sẽ cứ ngồi ì đấy cho tới khi ta subscribe, nó mới bắt đầu listen

(khá giống với function-declaring và function-invoking nhỉ).

Ví dụ
// 1. From event: MouseEvent stream (new event is created every time a click
let clicks = Rx.Observable.fromEvent(document, 'click');

// 2. From array: [10 20 30] stream
const array = [10, 20, 30]; 
let resultA = Rx.Observable.from(array); 

// 3. From Promise returned by fetch()
let resultP = Rx.Observable.fromPromise(fetch('http://haodev.wordpress.com/'));

 

Phân loại
Cold Observable

An observable is cold if its underlying producer is created and activated during subscription (inside subscription)

Ví dụ

// Anything, subscribes `source`, gets its own instance
const source = new Observable(observer => {
  const socket = new WebSocket('http://haodev.wordpress.com/');
  // ...
});

 

Hot Observable

An observable is hot if its underlying producer is either created or activated outside of subscription(outside subscription)

Ví dụ

// Anything, subscribes `source`, shares same instance, multicast to all subscriber
const socket = new WebSocket('http://haodev.wordpress.com/');
const source = new Observable(observer => socket.addEventListener(
  'message',
  e => observer.next(e)
));

Nhận xét

Observable là hot hay cold phụ thuộc vào việc Producer được tham chiếu hay là tạo mới; Nếu như rơi vào cả 2 trường hợp, có lẽ, nó là warm observable (J4F)

Observer

Định nghĩa

In Reactive programmingObserver subscribes to an Observable. Then that observer reacts to whatever item or sequence of items the Observable emits.

Observer chính là param trong phương thức Observable.subscribe(observer).

Trong Observer gồm các callbacks tương ứng: next()error() và complete()(không nhất thiết phải có đầy đủ tất cả) được gửi đến bởi Observable để xử lý các trạng thái tương ứng.

Ví dụ
// 1. Full callbacks
const observer = {
  next: x => console.log('got value ' + x),
  error: err => console.error('something wrong: ' + err),
  complete: () => console.log('done'),
};

// 2. Just an next function (like then() in Promise)
var observer = (x) => {
  console.log('Completed');
}

 

Subscription

Định nghĩa

Sau khi Observable được tạo ra, nó sẽ chưa listen streams ngay lúc đó. Để invoke một Observable, chúng ta phải subscribe nó.

Subscription được sinh ra khi ta subscribe Observable, nó thường dùng cho việc unsubscribe() Observable đó.

Ví dụ
let resultP = Rx.Observable.fromPromise(fetch('http://haodev.wordpress.com/'));

// With observer type 1
resultP.subscribe(x => console.log('got value ' + x))

// With observer type 2
resultP.subscribe(x => console.log(x), e => console.error(e));

// With observer type 3
resultP.subscribe({
  next: x => console.log('got value ' + x),
  error: err => console.error('something wrong: ' + err),
  complete: () => console.log('done'),
});

 

Khi ta không muốn listen stream đó nữa:

let subscriptionP = resultP.subscribe(observer);
subscriptionP.unsubscribe();

 

 

Okayyy, sau khi đã biết Observable là gì rồi, ta cùng tìm hiểu sang Operator nào

Operator

Định nghĩa

Operator is also JUST FUNCTION.

Với Operator, ta vẫn có thể lập trình hàm với ObservableOperator là một pure function với Input là Observable và Output là một Observable khác, giữ được Observable Input không bị thay đổi.

Khi chúng ta subscribe()/unsubscribe() Observable Output thì đồng thời cũng subscribe()/unsubscribe() Observable Input.

Tương tự với array có rất nhiều các phương thức thao tác hỗ trợ, Observable cũng có đa dạng các Operators với nhiều mục đích khác nhau như createfiltertransformcombinehandleutilsmulticast, etc.

Ví dụ
// Get maximal value of number series
Rx.Observable.of(5, 4, 7, 2, 8)
.map(num => num*10)
.max()
.subscribe(x => console.log(x)); // 80

 

Custom Operator

Ngoài các built-in operators, ta cũng có thể viết ra các custom operators:

function multiplyByTen(input) {
  let output = Rx.Observable.create(function subscribe(observer) {
    input.subscribe({
      next: v => observer.next(10 * v),
      error: err => observer.error(err),
      complete: () => observer.complete()
    });
  });
  return output;
}

const input = Rx.Observable.from([1, 2, 3, 4]);
const output = multiplyByTen(input);
output.subscribe(x => console.log(x)); // 10 20 30 40

 

Subject

Định nghĩa

In RxJS:

  • Plain Observables are unicast
  • Subject:
    • Be a special type of Observable that allows values to be multicasted to many Observer
    • Maintain a registry of many listeners/subscriber.

 

Hmmm… nghe khoai mì nhỉ? Bảo sao Hà Lan rầu vậy?

Để xem nào !!! Hãy dẹp Subject qua một bên, tìm hiểu Unicast vs. Multicast là gì đã nhé 

Unicast vs. Multicast
Key Unicast Multicast
Def. A transmission/stream sends IP packets to a single recipient on a network A transmission sends IP packets to a group of hosts on a network
Ex. Plain Observable RxJs Subject, Promises
Giải thích Nếu ta subscribe nhiều lần với một observable gốc thì nó sẽ không được chia sẻ. Nghĩa là, mỗi lần subcribe là bạn đang tạo ra một observable mới. Có thể subscribe nhiều lần với obseravle gốc.
Hình dung Bạn vào Youtube, mở video nào đó đã được thu sẵn và xem, nó play từ đầu đến cuối video. Một người khác vào xem, Youtube cũng sẽ phát từ đầu đến cuối như thế, hai người không có liên quan gì về thời gian hiện tại của video mà mình đang xem. 2++ người vào xem video ở Youtube, nhưng video đó đang phát Live (trận bóng đá U23 hôm qua chẳng hạn :v). Lúc này, những người bắt đầu click vào xem sẽ có cùng một thời điểm của video đó.

 

Như vậy, Subject là một plain observable đặc biệt có khả năng thực hiện việc gửi dữ liệu đến nhiều Observers (multicasting).

Nhận xét
  • Mỗi Subject là một ObservableVới mỗi Subject, ta có thể subscribe vào nó, cung cấp cho nó một Observer và có thể nhận data tương ứng.
  • Mỗi Subject là một ObserverBên trong Subject có chứa các method next()error()complete() tương ứng để có thể subscribe vào Observable chẳng hạn. Trường hợp cần gửi dữ liệu cho các Observers mà Subject đang quản lý, ta chỉ cần gọi hàm tương ứng của nó là được.

Notes

Tóm tắt lại thì, RxJS về bản chất là một thư viện nhỏ gọn trong Reactive Programming. Các tính năng khá phức tạp có thể được thực hiện dễ dàng với sự trợ giúp của Observables và các Operators.

Song, có một điểm cần lưu ý, khi subscribe một observable nhiều lần (đặc biệt là cold observable) thì vô hình chung sẽ tạo ra nhiều subscription tới một observable. Do đó, khi dùng tới Observable trong RxJS ta phải cực kì thận trọng tránh gây ra các vấn đề như memory leakresource leak,…

 

Cá nhân mình cảm thấy RxJS là thư viện khá là khó khi mới tiếp cận; thế nếu như có thể hiểu được bản chất rồi thì RxJS có thể trở thành một trợ thủ đắc lực ^^

The more you use it, the more you can do with it.

The end

Yeahhhh, sau khi điểm qua các từ khóa trên đây thì Hà Lan đã hiểu hơn về Reactive Programming và RxJS Concepts rồi nè
Mong rằng bài viết này có thể giúp ích được cho các bạn mới tiếp cận tới RxJS như mình và cô Mắt biếc nhé. Từ đó Hà Lan cũng không còn phải code draft lên cây nữa rồi.

TopDev via Viblo

 

 

 

 

 

Bạn có học thêm kỹ năng mới trong năm nay không?

View Results

Loading ... Loading ...