30 天精通 RxJS (07): Observable Operators & Marble Diagrams

这是【30天精通 RxJS】的 07 篇,如果还没看过 06 篇可以往这边走:
30 天精通 RxJS (06): 建立 Observable(二)

昨天我们把所有建立 Observable 实例的 operators 讲完了,接下来我们要讲关于转换(Transformation)、过滤(Filter)、合併(Combination)等操作方法。先来让我们看看什麽是 Operator

什麽是 Operator?

Operators 就是一个个被附加到 Observable 型别的函式,例如像是 map, filter, contactAll... 等等,所有这些函式都会拿到原本的 observable 并回传一个新的 observable,就像有点像下面这个样子

var people = Rx.Observable.of('Jerry', 'Anna');

function map(source, callback) {
    return Rx.Observable.create((observer) => {
        return source.subscribe(
            (value) => { 
                try{
                    observer.next(callback(value));
                } catch(e) {
                    observer.error(e);
                }
            },
            (err) => { observer.error(err); },
            () => { observer.complete() }
        )
    })
}

var helloPeople = map(people, (item) => item + ' Hello~');

helloPeople.subscribe(console.log);
// Jerry Hello~
// Anna Hello~

JSBin | JSFiddle

这裡可以看到我们写了一个 map 的函式,它接收了两个参数,第一个是原本的 observable,第二个是 map 的 callback function。map 内部第一件事就是用 create 建立一个新的 observable 并回传,并且在内部订阅原本的 observable。

当然我们也可以直接把 map 塞到 Observable.prototype

function map(callback) {
    return Rx.Observable.create((observer) => {
        return this.subscribe(
            (value) => { 
                try{
                    observer.next(callback(value));
                } catch(e) {
                    observer.error(e);
                }
            },
            (err) => { observer.error(err); },
            () => { observer.complete() }
        )
    })
}
Rx.Observable.prototype.map = map;
var people = Rx.Observable.of('Jerry', 'Anna');
var helloPeople = people.map((item) => item + ' Hello~');

helloPeople.subscribe(console.log);
// Jerry Hello~
// Anna Hello~

这裡有两个重点是我们一定要知道的,每个 operator 都会回传一个新的 observable,而我们可以透过 create 的方法建立各种 operator。

在 RxJS 5 的实作中,其实每个 operator 是透过原来 observable 的 lift 方法来建立新的 observable,这个方法会在新回传的 observable 物件内偷塞两个属性,分别是 source 与 operator,记录原本的资料源跟当前使用的 operator。

其实 lift 方法还是用 new Observable(跟 create 一样)。至于为什麽要独立出这个方法,除了更好的封装以外,主要的原因是为了让 RxJS 5 的使用者能更好的 debug。关于 RxJS 5 的除错方式,我们会专门写一篇来讲解!

这裡我们只是简单的实作 operator。如果之后实务上,想要不影响原本的 Observable 又能够自订 operator 可以参考官方的这份文件。(现在先不用看)

其实 RxJS 提供的各种 operators 已经非常够用了,不太需要我们自己创造 operator,这裡只是想让大家先对 operator 的建立有个基本的观念,之后在学习的过程中会比较轻鬆。

在我们开始介绍 RxJS 的 operators 前,为了能让我们更好地理解各种 operators,我们需要先订定一个简单的方式来表达 observable!

Marble diagrams

我们在传达事物时,文字其实是最糟的手段,虽然文字是我们平时沟通的基础,但常常千言万语也比不过一张清楚的图。如果我们能订定 observable 的图示,就能让我们更方便的沟通及理解 observable 的各种 operators!

我们把描绘 observable 的图示称为 Marble diagrams,在网路上 RxJS 有非常多的 Marble diagrams,规则大致上都是相同的,这裡为了方便撰写以及跟读者的留言互动,所以採用类似 ASCII 的绘画方式。

我们用 - 来表达一小段时间,这些 - 串起就代表一个 observable。

----------------

X (大写 X)则代表有错误发生

---------------X

| 则代表 observable 结束

----------------|

在这个时间序当中,我们可能会发送出值(value),如果值是数字则直接用阿拉伯数字取代,其他的资料型别则用相近的英文符号代表,这裡我们用 interval 举例

var source = Rx.Observable.interval(1000);

source 的图形就会长像这样

-----0-----1-----2-----3--...

当 observable 是同步送值的时候,例如

var source = Rx.Observable.of(1,2,3,4);

source 的图形就会长像这样

(1234)|

小括号代表著同步发生。

另外的 Marble diagrams 也能够表达 operator 的前后转换,例如

var source = Rx.Observable.interval(1000);
var newest = source.map(x => x + 1); 

这时 Marble diagrams 就会长像这样

source: -----0-----1-----2-----3--...
            map(x => x + 1)
newest: -----1-----2-----3-----4--...

最上面是原本的 observable,中间是 operator,下面则是新的 observable。

以上就是 Marble diagrams 如何表示 operator 对 observable 的操作,这能让我们更好的理解各个 operator。

Marble Diagrams 相关资源:http://rxmarbles.com/

最后让我们来看几个简单的 Operators!

Operators

map

Observable 的 map 方法使用上跟阵列的 map 是一样的,我们传入一个 callback function,这个 callback function 会带入每次发送出来的元素,然后我们回传新的元素,如下

var source = Rx.Observable.interval(1000);
var newest = source.map(x => x + 2); 

newest.subscribe(console.log);
// 2
// 3
// 4
// 5..

用 Marble diagrams 表达就是

source: -----0-----1-----2-----3--...
            map(x => x + 1)
newest: -----1-----2-----3-----4--...

我们有另外一个方法跟 map 很像,叫 mapTo

mapTo

mapTo 可以把传进来的值改成一个固定的值,如下

var source = Rx.Observable.interval(1000);
var newest = source.mapTo(2); 

newest.subscribe(console.log);
// 2
// 2
// 2
// 2..

mapTo 用 Marble diagrams 表达

source: -----0-----1-----2-----3--...
                mapTo(2)
newest: -----2-----2-----2-----2--...

filter

filter 在使用上也跟阵列的相同,我们要传入一个 callback function,这个 function 会传入每个被送出的元素,并且回传一个 boolean 值,如果为 true 的话就会保留,如果为 false 就会被滤掉,如下

var source = Rx.Observable.interval(1000);
var newest = source.filter(x => x % 2 === 0); 

newest.subscribe(console.log);
// 0
// 2
// 4
// 6..

filter 用 Marble diagrams 表达

source: -----0-----1-----2-----3-----4-...
            filter(x => x % 2 === 0)
newest: -----0-----------2-----------4-...

读者应该有发现 map, filter 这些方法其实都跟阵列的相同,因为这些都是 functional programming 的通用函式,就算换个语言也有机会看到相同的命名及相同的用法。

实际上 Observable 跟 Array 的 operators(map, filter),在行为上还是有极大的差异。当我们的资料量很大时,Observable 的效能会好上非常多。我们会有一天专门讲这个部份!

今日小结

今天我们讲了 Observable Operators 的相关知识,有以下几个重点

  • 什麽是 Operators
    • 如何建立 operator
  • Marble diagrams
  • Operators
    • map
    • mapTo
    • filter

不知道今天读者有没有收穫呢?欢迎在下方留言给我,这是精通 RxJS 的第 07 篇!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容