Một thoáng Observable
Observable là một cách để quản lý bất đồng bộ, mô hình này dựa trên Observable design pattern và nó cũng được ứng dụng nhiều trong thực tế.
Khái niệm Observable
Giống như cái tên của nó, Observable là “quan sát được”, nghĩa là nó là những hàm trả về các giá trị, đối tượng nào gọi observables thì cũng có thể theo dõi những giá trị đó. Về cơ bản thì observable tạo ra một hệ thống pub-sub sự trên mẫu thiết kế (design pattern) Observer.
RxJs là một thư viện rất nổi tiếng sử dụng mô hình này, tên của nó nghĩa là "Reactive Extension for JavaScript" (phản hồi hay đại loại cái gì đó tương tự - mở rộng cho JavaScript). Nói xa hơn một chút, RxJs dựa trên mô hình Reactive programming, quản lý luồng dữ liệu (stream), còn quản lý sao nữa thì hơi xa quá nội dung này.
Bản thân mình không dùng RxJs nhưng mà ở Manabie (công ty mình đang làm hiện tại) thì đội Frontend cũng sử dụng mô hình đại loại gần giống vậy (dựa theo Observer design pattern) để theo dõi các tác vụ khác nhau, đơn cử như theo dõi quá trình import dữ liệu và phản hồi cho user biết. Cách triển khai của Observable là viết một class Observable cung cấp một số phương thức hỗ trợ.
Tạo một Observable
Để cho dễ hình dung hơn thì chúng ta viết một cái class đơn giản sau
class Observable {
constructor(functionTakesObserver){
this._functionTakesObserver = functionTakesObserver;
}
subscribe(observer) {
return this._functionTakesObserver(observer)
}
}
Có vậy thôi, đừng ngạc nhiên. Là một chiếc class có tham số trong construction là hàm functionTakesObserver
Rồi mình thêm một phương thức subscribe()
subscribe(observer) {
return this._functionTakesObserver(observer)
}
nó cũng chỉ đơn giản là phương thức truyền tham số observer
vào cái hàm functionTakesObserver
là tham số trong construction ở trên.
Bắt chước theo RxJS thì ta cần một đối tượng observer, hay có thể gọi là đối tượng theo dõi được, nó chứa các hàm callback next
, error
, complete
và Observable có thể truyền thông tin vào các phương thức này.
const observer = {
next(data) {
console.log(data)
},
error(e) {
console.log(e)
},
complete() {
console.log("request complete")
}
}
Giờ chúng ta tạo một instance của Observable, mục đích là để gán cái observer ở trên vào, ta có:
const myObservable = new Observable(observer => {
setTimeout(() => {
observer.next("got data!")
observer.complete()
}, 1000)
})
Cách nhanh nhất để giả lập thời gian phản hồi của một HTTP request là dùng setTimeout()
, mỗi lần request xong là mình gọi next()
và complete()
để nhận thông báo.
Và cuối cùng là hành động, truyền cái observer object ở trên vào, tức là lúc này mình thực thi cái hàm truyền vào constructor và register (không biết tiếng Việt ghi sao cho đúng) các callback đã xác định trong observer
myObservable.subscribe(observer)
// (1 second) got data!
// (1 second) request complete
Tất nhiên, ở trên mình mô tả nguyên lý cơ bản thôi, còn trong thực tế thì team mình có nhiều pha xử lý phức tạp hơn xíu. Tùy vào cách mà mọi người muốn làm cái gì, ví dụ như là thêm, sửa, xóa các kiểu con đà điểu, truyền vào window object hay localStorage gì đó để có thể nhận notify ở các trang khác nhau.
Giờ mình trộn cái đống ở trên lại cho dễ nhìn
class Observable {
constructor(functionTakesObserver){
this._functionTakesObserver = functionTakesObserver;
}
subscribe(observer) {
return this._functionTakesObserver(observer)
}
}
const myObservable = new Observable(observer => {
setTimeout(() => {
observer.next("got data!")
observer.complete()
}, 1000)
})
const observer = {
next(data) {
console.log(data)
},
error(e) {
console.log(e)
},
complete() {
console.log("request complete")
}
}
myObservable.subscribe(observer)
Hết rồi :D