在以前读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
关键字。
做为一个英语不是很好的码农,翻译本文仍是有点磕磕碰碰,可是总算仍是勉勉强强搞定了,但愿可以帮助你们多了解一下这个特性,若有错误麻烦诸位指出一下,最后国际惯例,感谢各位的阅读~