Functional Reactive Programing với Rxjs
Khi làm việc với Angular 2 nói riêng cũng như xử lý bất đồng bộ (ASYNC) trong javascript nói chung, hẳn bạn đã từng nghe tới khái niệm Fucntional Reactive Programing. Vậy FRP là gì mà thời gian gần đây hot đến vậy? Thậm chí nhiều người cho rằng nó sẽ thay thế OOP trong tương lai nữa.Trong bài viết ...
Khi làm việc với Angular 2 nói riêng cũng như xử lý bất đồng bộ (ASYNC) trong javascript nói chung, hẳn bạn đã từng nghe tới khái niệm Fucntional Reactive Programing. Vậy FRP là gì mà thời gian gần đây hot đến vậy? Thậm chí nhiều người cho rằng nó sẽ thay thế OOP trong tương lai nữa.Trong bài viết này chúng ta sẽ cùng có cái nhìn tổng quát, cũng như ví dụ về nó. Nếu bạn đang tìm kiếm 1 sự so sánh giữa FRP và OOP thì không phải đây đâu nhé.
1. Reactive Programing ?
Trong các ứng dụng web và mobile, người dùng có thể tương tác với dữ liệu thông qua UI (User Interface). 10 năm trước, việc này với các trang web đơn thuần là việc submit 1 form lên server backend rồi dựng 1 trang giao diện cho frontend. Chẳng vui vẻ gì với việc cứ ngồi bấm F5 mãi xem nội dung đã được cập nhật chưa. Ngày nay, cả nhu cầu của người dùng lẫn ứng dụng đều phát triển theo hướng real-time: Việc thay đổi 1 form data, like 1 bài viết hay kết bạn,… đều ngay lập tức được phản ánh theo thời gian thực (real-time), điều này mang lại trải nghiệm người dùng tuyệt vời hơn bao giờ hết. Đỉnh cao của Real-time chắc hẳn là những ứng dụng mạng xã hội như Facebook, Twitter,...
Đó là về phía user, còn về phía những developers, việc xử lý lý những sự kiện real-time này chúng ta cần đến Reactive Programing. Reactive Programing không phải con đường duy nhất nhưng nó là cách tốt nhất hiện nay để tiếp cận với ứng dụng real-time. Và mặc dù Reactive Program không chỉ là để lập trình real-time program mà còn nhiều ứng dụng khác nữa ví dụ như protocols, system drivers, … nhưng trong khuôn khổ bài viết chúng ta sẽ tập trung vào các ứng dụng real-time.
2. Functional Reative Programing bằng Reactive Extension
Reactive Extension (Rx) là thư viện nhằm ứng dụng Reactive Programing vào xử lý ASYNC và các chương trình hướng-sự kiện sử dụng Observable (là gì hồi sau sẽ rõ) và các toán tử theo phong cách LINQ. Trong angular 2, việc sử dụng Reactive Programing sẽ giúp bạn cải thiện rất nhiều code trong xử lý ASYNC, đặc biệt khi bạn gọi request lên server (nodejs ,php,..).
Giờ đây thay vì code hướng xử lý cụ thể cho từng request thì bạn có thể tập trung hơn vào logic trừu tượng, đưa ra hướng giải quyết vấn đề.
Rx thường được gọi là “functional reactive programing”, nhưng bạn cần hiểu rõ và tách biệt 2 khái niệm này. Rx mang đặc điểm của Reactive Programing và nó cũng có thể áp dụng Functional Programing vào nhưng về bản chất thì đây là 2 paradigm khác nhau.
Rxjs là 1 thể hiện của Rx bằng ngôn ngữ javascript (đây cũng là ngôn ngũ event-driven phổ biến nhất hiện nay). Tóm gọn lại thì muốn lập trình reactive với javascript thì cái bạn cần là lên trang này
https://github.com/ReactiveX/rxjs
và download về dự án của mình. Nếu bạn sử dụng angular 2 thì rxjs đã được tích hợp sẵn trong bộ thư viện rồi.
3. Rxjs
Rxjs định nghĩa:
**Reactive Programing là lập trình với các dòng chảy dữ liệu bất đồng bộ (async data streams)**
Mình rất thích cách định nghĩa này của Rxjs, hơn những gì phức tạp và lằng nhằng trên wiki hay trang chủ ReactExtension, hiểu theo cách này, mọi thứ không có gì mới. Tất cả những event hay kể cả những click chuột thông thường cũng là 1 async data stream. Và tuyệt hơn là bạn có thể quan sát (observer) stream đó và thực thi các hành động tương ứng. Bạn có thể biến mọi thứ thành data stream: các biến, user input, cấu trúc dữ liệu ,…
Nếu vẫn còn quá trừu tượng, bạn hãy tưởng tượng new feed facebook của mình là 1 data stream, mỗi khi có người post bài thì data stream của bạn sẽ có thêm bài post đó, và bạn chính là người quan sát data stream này, khi có 1 post ảnh món ăn bạn thích bạn click like , đây cũng như là observer perform 1 action dựa vào data stream.
Hơn thế nữa, bạn được rxjs hỗ trợ 1 công cụ tuyệt vời để bạn có thể mô tả về bức ảnh món ăn mình sẽ like (làm sao mà rxjs có thể phân biệt được bạn chỉ muốn like ảnh thịt bò chứ không phải thịt gà? tất nhiên bạn phải ra chỉ thị cho nó) đấy chính là lúc functional programing tham gia vào. Functional programing giúp bạn tạo ra ,gộp, filter,… 1 data stream hay thậm chí biến 1 stream này thành 1 stream khác.
Như vậy data stream là trung tâm của Reactive, hãy cùng xem 1 ví dụ về “click chuột” data-stream.
Data-stream này là 1 chuỗi các sự kiện click chuột được sắp xếp theo thứ tự thời gian. Và các sự kiện này có thể là 1 trong 3 thứ :
- 1 giá trị (click được ghi nhận)
- 1 lỗi
- 1 tín hiệu complete ( người dùng close trình duyệt )
Chúng ta ghi nhận những events này 1 cách không đồng bộ bằng cách khai báo 3 functions tương ứng với mỗi loại sự kiện value - error – complete.Các functions này sẽ được thực thi khi có 1 sự kiện mới được đưa vào Observable. Và bởi vì Rx dựa trên Observer Design Patern, nên chúng ta sẽ gọi tên cho phù hợp 1 chút :
- Data-stream => Observable
- Các functions => Observer
- Việc các functions lắng nghe sự kiện từ data-stream => Subscribing
Ngoài ra, tôi cũng sẽ có lúc sử dụng biểu đồ dạng mã ASCII như sau, nó cũng có ý nghĩa tương tự như diagram
--a---b-c---d---X---|-> a, b, c, d là những giá trị X is là lỗi | is là tín hiệu complete ---> là dòng thời gian
OK, vậy là bây giờ tôi có 1 data-stream những cú click chuột, data-stream này sẽ thay đổi theo thời gian và hành vi người dùng. Nhưng cái data-stream này có tác dụng gì? Bây giờ những gì bạn ghi nhận được từ data-stream này 1 chuỗi raw-data về các event click chuột, nếu đơn giản là thêm observer vào để kích hoạt mỗi khi có click chuột mới thì nó cũng chẳng khác gì jQuery(đâu nhất thiết phải đẻ thêm 1 loạt khái niệm phức tạp phải không?)
Và để show-off sức mạnh thực sự của Rx, tôi đưa cho các bạn 1 bài toán mới: Mỗi lần double click chuột thì sẽ có hiệu ứng aura xung quanh con chỏ. như kiểu thế này:
Phức tạp hơn 1 chút, tôi muốn những multiple click cũng đều tương đương với double click, cùng với đó những double click chuột (dưới 250ms) mới được xem là hợp lệ, chẳng có lý nào 2 click chuột cách nhau đến 1 phút lại có tác dụng được.
Nếu bạn đã quen với jQuery hãy tưởng tượng số lượng code cũng như logic bạn sẽ phải viết? Thay vì tập trung vào làm hiệu ứng sao cho đẹp thì bạn phải làm việc vất vả để định nghĩa ra 1 event double click khá lằng nhằng với các biến và các hàm time interval của js.
Tất nhiên chúng ta sẽ không làm như vậy, việc định nghĩa event double click bạn cứ quẳng cho rxjs lo hãy cứ nghĩ xem làm sao hiệu ứng cho đẹp. Thực tế, chỉ cần 4 dòng code để xử lý logic với rxjs. Nhưng bây giờ chúng ta tạm thời bỏ qua code, điều tôi muốn bây giờ là các bạn thử suy nghĩ theo kiểu reactive (thinking in reactive).RFP paradigm nâng level trừu tượng trong code của bạn lên 1 tầm cao mới, nơi bạn “thực sự” focus vào các events để đưa ra bussiness logic tương ứng, hơn là xử lý ở mức độ thể hiện từng chi tiết nhỏ. Code bằng RFP ngắn gọn súc tích và dương như cũng pro hơn nhiều.
Đầu tiên hãy nhớ lại tôi đã nói 1 data-stream có thể biến thành 1 data-stream khác. Rxjs giúp chúng ta làm điều này thông qua rất nhiều funtions đi kèm (map, filter, scan,..) Khi bạn gọi các functions này nó trả về 1 data-stream mới dựa trên data-stream có sẵn, data-stream có sẵn của bạn vẫn không thay đổi. Tính chất này được gọi là immutability(bất biến) trong functional programing, trong rxjs thì Functional Programing kết hợp với Reactive Programing 1 cách hoàn hảo.
Những ô màu xám là những functions làm nhiệm vụ biến đổi 1 data-stream thành 1 data-stream khác. Để giải quyết bài toán trên, ta đi qua 4 bước.
- Ghi nhận click chuột vào 1 data-stream
- Chúng ta gôp các event trong khoảng 250ms thành 1 giá trị
- Lấy số lượng event của mỗi giá trị để làm giá trị của stream mới
- Lọc lấy những giá trị > 2
Giờ thì công việc cuối cùng chỉ là subscribe 1 event listener thông qua observer để perform những hiệu ứng mà chúng ta muốn. Tất nhiên ví dụ này chỉ là bề nổi của tảng băng chìm: Bạn có thể áp dụng Rxjs cho rất nhiều loại stream, thậm chí là các response trả về từ 1 API. Bài này tôi chỉ giới hạn ví dụ tại đây những ví dụ chi tiết và thực hành để những bài sau nhé, điều tôi muốn nhấn mạnh lúc này là hãy thay đổi cách nghĩ theo hướng reactive, thì việc học RFP sẽ rất dễ dàng.
4. Các khái niệm trong Rxjs
- Observable: Đại diện cho 1 collection các sự kiện hoặc giá trị trong tương lai
- Observer: 1 tập hợp functions lắng nghe Observable
- Subscription: Đại diện cho sự thực thi việc lắng nghe 1 Observable, chủ yếu sử dụng để dừng thao tác subscribe
- Operators: Các functions nhằm áp dụng Functional Programing vào Rxjs
- Subject: tương đương với 1 EventEmitter, là cách duy nhất để multicasting event tới nhiều Observers, (Subject mang tính chất của cả Observable và Observer)
- Schedulers: Sử dụng để giúp chúng ta kiểm soát các thao tác đồng thời (như các hàm setTimeout, requestAnimationFrame,…)
5. Pull và Push data
Pull và push là 2 giao thức khác nhau, mô tả 1 data Producer làm cách nào giao tiếp với 1 data Consumer
Trong pull system: Consumer quyết định lúc nào cần nhận data từ Producer, Producer không biết lúc nào data sẽ được gửi tới Consumer. Mọi function trong js là Pull. Các function sản sinh ra data (producer), và đoạn code gọi function là nơi tiêu thụ (consumer)
Producer | Consumer | |
---|---|---|
Pull | Passive: produces data when requested. | Active: decides when data is requested. |
Push | Active: produces data at its own pace. | Passive: reacts to received data. |
-------- | -------- | -------- |
Ngược lại với Push system thì Producer quyêt định khi nào gửi data cho Consumer, consumer không được biết về điều này. Promises là 1 Push system. Nhưng giới hạn của promise chỉ có 1 giá trị trả về. Observable sinh ra giải quyết nhược điểm đó và nó cũng lấp đầy chỗ trống trong bảng sau
Observable sản sinh đa giá trị (Producer) và push nó tới các Observer (Consumer).
Kết luận :
Ở bài này chúng ta đã làm quen với 1 số khái niệm về Reactive Function Programing. Việc học RFP thực sự là khó ngay cả với những ai đã quen với lập trình Imperative Programing, nếu bạn nghĩ bạn có thành thạo sử dụng nó trong 24h thì quên chuyện đó đi. Học RFP cần thời gian, việc khó nhất là thay đổi cách suy nghĩ thông thường hay là “Thinking in Reactive”, bắt ép bộ não làm việc theo 1 mô hình mới nhưng 1 khi đã quen thì bạn sẽ thấy RFP thực sự rất tuyệt vời (thậm chí đã đến lúc suy nghĩ nghiêm túc về việc vứt sọt rác cái mớ OOP đi). Bài sau chúng ta sẽ đi tiếp đến 1 số khái niệm nâng cao hơn cũng như làm thử 1 ứng dụng Kết bạn giống như Twitter và cũng xin cảm ơn vì đã đọc đến tận những dòng này