在以前讀redux
源碼時,遇到了關於Symbol.observable
的使用,發現從沒有看到過這個特性,在國內的技術論壇上逛了許久發現說起此的文章甚少,恰巧今天在摸魚時發現了一篇聊ECMAScript
中新提案observables
的文章,故翻譯出來加深印象~javascript
原文點此java
ps: 在下文中將以 ES 代替 ECMAScriptgit
本文介紹的是當前還在ES提案階段中的observables特性,在下文中經過本文,咱們將帶你瞭解該提案,該提案的api,以及一些使用案例github
在寫下這篇文章的時候,javascript
的Observables(觀察)
正在經過RXJs、Bacon.js
等各類類庫逐漸普及。Jafar Husain,這位長久以來主張函數式編程的Netfix
的技術leader(同時也是TC39的委員)也提出了將observables
集成到咱們js core
中的議案,而且已經經過了stage1(徵求意見階段)
且已經肯定該提案即將進入stage2(草案階段)
。編程
Observable
和 observer
的api在當前提案中,Observable
是一個內建的被用來處理事件流的類,Obsservalbe
的構造函數能夠接受一個定義事件流的回調函數。在接下來的例子中,咱們的observable
將只返回值爲1或者2的事件流。observer.next
方法是用來在observalbe
流中添加事件的:redux
new Observable(observer => {
observer.next(1);
observer.next(2);
})
複製代碼
咱們也能夠使用observer.error
來記錄在流處理時遇到的錯誤:api
new Observable(observer => {
observer.error(new Error(`Failed to stream events`))
})
複製代碼
咱們還能夠使用observer.complete
來在流處理完結的時候發出信號:瀏覽器
new Observable(observer => {
observer.next(1)
observer.next(2)
observer.complete()
})
複製代碼
傳遞給咱們Observable
構造函數的這個回調函數會返回一個清理咱們Observable實例的方法,它能夠執行清理事件監聽,定時任務等等相似的清理任務。舉個例子,固然這個例子就要比上面這些有趣的多了,他追蹤了用戶在移動鼠標時,光標相對於頁面的位置,並同時產生了描述當前光標座標的事件流:bash
function mouseTracking () {
return new Observable(observer => {
const handler = ({ pageX, pageY }) => {
observer.next({ x: pageX, y: pageY })
}
document.body.addEventListener(`mousemove`, handler)
return () => {
document.body.removeEventListener(`mousemove`, handler)
}
})
}
複製代碼
爲了訂閱一個Observable
的事件流,咱們會使用Observable
實例上的subscribe
方法,這樣作會調用咱們以前實例化Observable
時傳入的回調函數,綁定事件的監聽,而且啓動整個事件流。這樣作以後咱們就能在移動鼠標的時候在事件流裏捕獲到它啦:ecmascript
mouseTracking().subscribe({
next({ x, y }) { console.log(`New position: ${ x }, ${ y }`) },
error(err) { console.log(`Error: ${ err }`) },
complete() { console.log(`Done!`) }
})
複製代碼
每次訂閱咱們都會生成一個訂閱對象Subscription
,這個訂閱對象上會有一個unsubscribe
方法讓咱們用來取消訂閱,執行清理方法(我猜你們應該都還記着以前提到的清理函數吧~),當咱們再也不須要關注觀察流裏的事件的時候,just unsubscribe it,讓咱們將其解放吧。
const subscription = mouseTracking().subscribe({
next({ x, y }) { console.log(`New position: ${ x }, ${ y }`) },
error(err) { console.log(`Error: ${ err }`) },
complete() { console.log(`Done!`) }
})
subscription.unsubscribe()
複製代碼
Observable.of(...items)
是一個簡單有效的能幫助咱們從提供的items
中建立Observable
的方法,在使用了Observable.of
方法以後,items
生成的Observable
實例會在調用subscribe
的同時生成事件流,返回items
中的value
:
Observable.of(1, 2, 3, 4).subscribe({
next(item) { console.log(item) }
})
// <- 1
// <- 2
// <- 3
// <- 4
複製代碼
咱們甚至能夠認爲,Observable.of
能夠理解爲跟如下接受一個入參,而後回傳事件流的簡單例子同樣:
Observable.of = (...items) => {
return new Observable(observer => {
items.forEach(item => {
observer.next(item)
})
observer.complete()
})
}
複製代碼
Observable.from
靜態方法接受一個類型爲對象的入參,若是這個對象中有鍵值爲Symbol.observable
的方法,那麼就會返回這個方法的返回值。
Observable
.from({
[Symbol.observable]() { return Observable.of(1, 2, 3) }
})
.subscribe({
next(item) { console.log(item) }
})
// <- 1
// <- 2
// <- 3
複製代碼
固然若是這個傳入的對象沒有實現Symbol.observable
,那麼咱們就假定其傳入的是一個可迭代的元素,Observable.from
在這個時候的做用就是將迭代元素遍歷並生成一個Observable
實例,依次將被遍歷的結果放入事件流中:
Observable
.from([1, 2, 3])
.subscribe({
next(item) { console.log(item) }
})
// <- 1
// <- 2
// <- 3
複製代碼
在這種狀況下,咱們的Observable.from
實現的功能和Observable.of
是相似的,據此咱們甚至能夠這樣去理解Observable.from
的實現:
Observable.from = value => {
if (typeof value[Symbol.observable] === `function`) {
return value[Symbol.observable]()
}
return Observable.of(Array.from(value))
}
複製代碼
雖然如今這個提案還在襁褓之中,可是我相信早晚有一天,其會成爲javascript
的函數式編程的基石。到那天,我相信它還會具備相似filter
和map
的能力去處理咱們的事件流,讓咱們在龐大的事件流可以僅僅注重咱們須要關注的部分就夠了
與此同時,咱們的代碼格式和開發模式也能在其幫助下變得更加的天然和規範,固然你也能夠提早使用咱們在github上的的polyfill
去提早體驗它,可是請切記在瀏覽器環境下刪除掉你的export
關鍵字。
做爲一個英語不是很好的碼農,翻譯本文仍是有點磕磕碰碰,可是總算仍是勉勉強強搞定了,但願可以幫助你們多瞭解一下這個特性,若有錯誤麻煩諸位指出一下,最後國際慣例,感謝各位的閱讀~