[譯] ECMAScript 的 Observables 提案

開始以前

在以前讀redux源碼時,遇到了關於Symbol.observable的使用,發現從沒有看到過這個特性,在國內的技術論壇上逛了許久發現說起此的文章甚少,恰巧今天在摸魚時發現了一篇聊ECMAScript中新提案observables的文章,故翻譯出來加深印象~javascript

原文點此java

ECMAScript 中的 Observables 提案

ps: 在下文中將以 ES 代替 ECMAScriptgit

本文介紹的是當前還在ES提案階段中的observables特性,在下文中經過本文,咱們將帶你瞭解該提案,該提案的api,以及一些使用案例github

在寫下這篇文章的時候,javascriptObservables(觀察)正在經過RXJs、Bacon.js等各類類庫逐漸普及。Jafar Husain,這位長久以來主張函數式編程的Netfix的技術leader(同時也是TC39的委員)也提出了將observables集成到咱們js core中的議案,而且已經經過了stage1(徵求意見階段)且已經肯定該提案即將進入stage2(草案階段)編程

Observableobserver 的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!`) }
})
複製代碼

訂閱對象上的 unsubscribe

每次訂閱咱們都會生成一個訂閱對象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

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

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的函數式編程的基石。到那天,我相信它還會具備相似filtermap的能力去處理咱們的事件流,讓咱們在龐大的事件流可以僅僅注重咱們須要關注的部分就夠了

與此同時,咱們的代碼格式和開發模式也能在其幫助下變得更加的天然和規範,固然你也能夠提早使用咱們在github上的的polyfill去提早體驗它,可是請切記在瀏覽器環境下刪除掉你的export關鍵字。

多個嘴

做爲一個英語不是很好的碼農,翻譯本文仍是有點磕磕碰碰,可是總算仍是勉勉強強搞定了,但願可以幫助你們多瞭解一下這個特性,若有錯誤麻煩諸位指出一下,最後國際慣例,感謝各位的閱讀~

相關文章
相關標籤/搜索