30 天精通 RxJS (06): 建立 Observable(二)

RxJS Logo

通常我们会透过creation operator来建立Observable实例,这篇文章会讲解几个较为常用的operator!

Creation Operator

Observable 有许多创建实例的方法,称为 creation operator。 下面我们列出 RxJS 常用的 creation operator

  • create
  • of
  • from
  • fromEvent
  • fromPromise
  • never
  • empty
  • throw
  • interval
  • timer

of

还记得我们昨天用create来建立一个同步处理的 observable 吗?

var source = Rx.Observable.create(function (observer) {
	observer.next('Jerry')
	observer.next('Anna')
	observer.complete()
})

source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})
// Jerry
// Anna
// complete!

他先后传递了'Jerry'、'Anna', 然后结束(complete),这是一个十分常见模式。 当我们想要同步的传递几个值时,就可以用`of 这个 operator 来简洁的表达!

下面的代码行为同上

var source = Rx.Observable.of('Jerry', 'Anna');
source.subscribe({
    next: function(value) {
        console.log(value)
    },
    complete: function() {
        console.log('complete!');
    },
    error: function(error) {
        console.log(error)
    }
});
// Jerry
// Anna
// complete!

是不是相较于原本的代码简洁许多呢?

from

可能已经有人发现其实 of operator 的一个一个参数其实就是一个 list,而 list 在 JavaScript 中最常见的形式是阵列(array),那我们有没有办法把一个已存在的阵列当作参数呢?

有的,我们可以用from来接收任何可列举的参数!

var arr = ['Jerry', 'Anna', 2016, 2017, '30 days']
var source = Rx.Observable.from(arr)
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})
// Jerry
// Anna
// 2016
// 2017
// 30 days
// complete!

记得任何可列举的参数都可以用喔,也就是说像 Set, WeakSet, Iterator 等都可以当作参数!

因为 ES6 出现后可列举(iterable)的类型变多了,所以fromArray就被移除咯。

另外 from 还能接收字符串(string),如下

var source = Rx.Observable.from('铁人赛')
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})
// 铁
// 人
// 赛
// complete!

上面的代码将把字符串中的每个字符一一印出来。

我们也可以传入 Promise 对象,如下

var source = Rx.Observable.from(
	new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve('Hello RxJS!')
		}, 3000)
	})
)

source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})
// Hello RxJS!
// complete!

如果我们传入 Promise 对象实例,当正常回传时,就会被送到 next,并立即送出完成通知,如果有错误则会送到 error。

这里也可以用fromPromise,会有相同的结果。

fromEvent

我们也可以用 Event 建立 Observable,透过的fromEvent方法,如下

var source = Rx.Observable.fromEvent(document.body, 'click')
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})
// MouseEvent {...}

fromEvent 的第一个参数要传入 DOM 对象,第二个参数传入要监听的事件名称。 上面的程序会针对 body 的 click 事件做监听,每当点击 body 就会印出 event。

取得 DOM 对象的常用方法: document.getElementById() document.querySelector() document.getElementsByTagName() document.getElementsByClassName()

补充:fromEventPattern

要用 Event 来建立 Observable 实例还有另一个方法fromEventPattern,这个方法是给类事件使用。 所谓的类事件就是指其行为跟事件相像,同时具有注册监听及移除监听两种行为,就像 DOM Event 有addEventListenerremoveEventListener一样! 举一个例子,我们在【30 天精通 RxJS (04): 什么是 Observable ?】实作的 Observer Pattern 就是类事件,代码如下:

class Producer {
	constructor() {
		this.listeners = []
	}
	addListener(listener) {
		if (typeof listener === 'function') {
			this.listeners.push(listener)
		} else {
			throw new Error('listener 必須是 function')
		}
	}
	removeListener(listener) {
		this.listeners.splice(this.listeners.indexOf(listener), 1)
	}
	notify(message) {
		this.listeners.forEach((listener) => {
			listener(message)
		})
	}
}
// ------- 以上都是之前的程式码 -------- //
var egghead = new Producer()
// egghead 同时具有 注册监听者及移除监听者 两种方法
var source = Rx.Observable.fromEventPattern(
	(handler) => egghead.addListener(handler),
	(handler) => egghead.removeListener(handler)
)

source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})
egghead.notify('Hello! Can you hear me?')
// Hello! Can you hear me?

上面的代码可以看到,eggheadProducer的实例,同时具有 注册监听及移除监听两种方法,我们可以将这两个方法依序传入fromEventPattern来建立 Observable 的对象实例!

这里要注意不要直接将方法传入,避免 this 出错!也可以用bind来写。

Rx.Observable.fromEventPattern(
	egghead.addListener.bind(egghead),
	egghead.removeListener.bind(egghead)
).subscribe(console.log)

empty, never, throw

接下来我们要看几个比较无趣的 operators,之后我们会讲到很多 observables 合并(combine)、转换(transforme)的方法,到那个时候无趣的 observable 也会很有用!

有点像是数学上的零(0),虽然有时候好像没什么,但却非常的重要。 在 Observable 的世界里也有类似的东西,像是empty

var source = Rx.Observable.empty()
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})
// complete!

这里可以看到 subscribe 是一个 function,这个 function 执行时会传入观察者,而我们在这个 function 内部去执行观察者的方法。

empty 会给我们一个空的 observable,如果我们订阅这个 observable 会发生什么事呢? 它会立即送出 complete 的讯息!

可以直接把empty想成没有做任何事,但它至少会告诉你它没做任何事。

数学上还有一个跟零(0)很像的数,那就是无穷(∞),在 Observable 的世界里我们用never来建立无穷的 observable

var source = Rx.Observable.never()
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log(error)
	},
})

never 会给我们一个无穷的 observable,如果我们订阅它又会发生什么事呢?... 什么事都不会发生,它就是一个一直存在但却什么都不做的 observable。

可以把 never 想象成一个结束在无穷久以后的 observable,但你永远等不到那一天!

题外话,笔者一直很喜欢平行线的解释: 两条平行线就是它们相交于无穷远

最后还有一个throw operator,它也就只做一件事就是抛出错误。

var source = Rx.Observable.throw('Oop!')
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log('Throw Error: ' + error)
	},
})
// Throw Error: Oop!

上面这段代码就只会 log 出'Throw Error: Oop!'

这三个 operators 虽然目前看起来没什么用,但之后在文章中大家就会慢慢发掘它们的用处!

interval, timer

接着我们要看两个跟时间有关的 operators,在 JS 中我们可以用setInterval来建立一个持续的行为,这也能用在 Observable 中

var source = Rx.Observable.create(function (observer) {
	var i = 0
	setInterval(() => {
		observer.next(i++)
	}, 1000)
})
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log('Throw Error: ' + error)
	},
})
// 0
// 1
// 2
// .....

上面这段代码,会每隔一秒送出一个从零开始递增的整数,在 Observable 的世界也有一个 operator 可以更方便地做到这件事,就是 interval

var source = Rx.Observable.interval(1000)
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log('Throw Error: ' + error)
	},
})
// 0
// 1
// 2
// ...

interval 有一个参数必须是数值(Number),这的数值代表发出信号的间隔时间(ms)。 这两段代码基本上是等价的,会持续每隔一秒送出一个从零开始递增的数值!

另外有一个很相似的 operator 叫timertimer可以给两个参数,范例如下

var source = Rx.Observable.timer(1000, 5000)
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log('Throw Error: ' + error)
	},
})
// 0
// 1
// 2 ...

timer有两个参数时,第一个参数代表要发出第一个值的等待时间(ms),第二个参数代表第一次之后发送值的间隔时间,所以上面这段代码会先等一秒送出 0 之后每五秒送出 1,2, 3, 4...。

timer 第一个参数除了可以是数值(Number)之外,也可以是日期(Date),就会等到指定的时间在发送第一个值。

另外timer也可以只接收一个参数

var source = Rx.Observable.timer(1000)
source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log('Throw Error: ' + error)
	},
})
// 0
// complete!

上面这段代码就会等一秒后送出 1 同时通知结束。

Subscription

今天我们讲到很多 无穷的 observable,例如 interval, never。 但有时我们可能会在某些行为后不需要这些资源,要做到这件事最简单的方式就是unsubscribe

其实在订阅 observable 后,会回传一个 subscription 对象,这个物件具有释放资源的方法unsubscribe,范例如下

var source = Rx.Observable.timer(1000, 1000)
// 取得 subscription
var subscription = source.subscribe({
	next: function (value) {
		console.log(value)
	},
	complete: function () {
		console.log('complete!')
	},
	error: function (error) {
		console.log('Throw Error: ' + error)
	},
})
setTimeout(() => {
	subscription.unsubscribe() // 停止订阅(退订), RxJS 4.x 以前的版本用 dispose()
}, 5000)
// 0
// 1
// 2
// 3
// 4

这里我们用了setTimeout在 5 秒后,执行了subscription.unsubscribe()来停止订阅并释放资源。另外 subscription 对象还有其他合并订阅等作用,这个我们之后有机会会在提到!

Events observable 尽量不要用unsubscribe,通常我们会使用takeUntil,在某个事件发生后来完成 Event observable,这个部份我们之后会讲到!

今日小结

今天我们把建立 Observable 实例的方法几乎都讲完了,建立 Observable 是 RxJS 的基础,接下来我们会讲转换(Transformation)、过滤(Filter)、合并(Combination)等 Operators,但不会像今天这样一次把一整个类型的 operator 讲完,笔者会依照实用程度以及范例搭配穿插着讲各种 operator!

本系列仅作为学习记录所用,摘录自30天精通Rxjs!强烈推荐!膜拜大佬!

posted @   楚小九  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示