RxJS系列教程(九) 操作异步流

Rx,不管你是JS,Java,Python还是Swift,玩的就是操作符。每个操作符怎么用,官方文档写得不能再清楚了,再配上例子和图,您要还整不明白就……继续整,整到明白为止,量变肯定会导致质变,就像国足永远不争气,都是永恒的真理。

然而整明白了操作符是干啥的,确不知道在啥地方用,这就是cookbook或者recipe之类书籍有市场的原因。说白了就像中国的教育,理论都是大拿,实践都是newbie。

当然,概念还是要讲的,理论是基础,实践是真理。下面我们来看一个场景:一个web app,电脑上用,用鼠标,手机平板上用,用手指,这不废话么!然而懂行的人知道里面有奥妙,用鼠标的,会触发mousedown,mouseup,mousemove事件,用手指的,会触发touchstart,touchend,touchmove事件。抛出来的问题是,能不能只写一套逻辑供两套事件使用呢?当然能了,拷贝粘贴呗。我们来看看RxJS中怎么做。

const mouseupStream = Rx.Observable.fromEvent(document, 'mouseup');// 鼠标按键抬起事件流
const touchendStream = Rx.Observable.fromEvent(document, 'touchend');// 触摸屏幕抬起事件流

mouseupStream.subscribe(/*处理逻辑*/);
touchedStream.subscribe(/*处理逻辑*/);

这不和没用RxJS一个意思么。别着急啊,没说这是最佳解决方案啊,接着往下看。

这两个事件是非常类似的,既然类似,我们能不能把它们当成一样的事件,合并起来呢?答案是,必须可以!

RxJS中的merge()操作符就是用来合并两个流的,既然是两个,就会有顺序问题,如果是同步操作,那就是有序的;如果是异步操作,merge()操作符内部会根据时间来做决定,合并起来的流中的事件就是无序的,交叉出现的。这里说句题外话,其实将操作符都要配图的,但是我实在是懒,况且图也不是我画的,大家就自己上网站上看吧。

merge()既有静态方法实现也有实例方法实现,以后讲操作符不额外声明的话,默认都有两种实现。

Rx.Observable.merge(mouseupStream, touchendStream);
//或
mouseupStream.merge(touchendStream);

假如我们最终的需求是要鼠标点击或手指触摸位置的数据,请看代码:

Rx.Observable.merge(mouseupStream, touchendStream)
  .do(event => console.log(event.type))// debug,查看事件类型
  .map(event => {
    switch (event.type) {
      case 'touchend':
        return {
          left: event.changedTouches[0].clientX,
          top: event.changedTouches[0].clientY
        };
      case 'mouseup':
        return {
          left: event.clientX,
          top: event.clientY
        }
    }
  })
  .subscribe(object => {
    console.log(`位置坐标为:(${object.left}, ${object.top})`);
  })

我们知道Rx一脉相承自函数式编程,那这段代码就有点说不过去了,怎能出现命令式的控制语句呢,说的就是你switch!当然理想化的东西能不能实现还是一回事儿呢,况且规则是人定的。但我们尽量在操作符中不出现命令式语句,把不得不出现的逻辑推迟到observer端来处理。我在系列三中提到过副作用也都放到observer来处理。

当然更好的方式是,我们在observable端就把数据处理好,observer接收时就不用再做处理了。

const pmouseupStream = mouseupStream.map(event => ({
  left: event.clientX,
  top: event.clientY
}));

const ptouchendStream = touchendStream.map(event => ({
  left: event.changedTouches[0].clientX,
  top: event.changedTouches[0].clientY
}));

Rx.Observable.merge(pmouseupStream, ptouchendStream)
  .subscribe(object => {
    console.log(`位置坐标为:(${object.left}, ${object.top})`);
  });

完美!

没有任何事会一直完美下去的。如果我们想要两个异步流合并后保持先后顺序呢?没问题,concat()操作符完美解决你的问题。这个没啥可讲的,同步流能用吗?您自己试试吧,实践才是真理。

继续继续。刚才命令式的switch被我们痛骂了一顿,RxJS中的switch()可就是个宝儿喽。叫李嘉诚的千千万,然而首富就那么一个,这就是差距。

switch()操作符只有实例方法实现方式。它的作用是切换到最新的那个observable。这到底是啥意思呢,请看图:

switch操作符
switch操作符

当我们第一次点击按钮的时候,map中的函数返回值又是一个observable,也就是说,switch接收到的是一个内嵌observable的observable,这时候switch会用内嵌的observable取代外层的那个observable,也就是click事件流被新产生的时间事件流取代了。

当我们在第二次点击按钮的时候(这个时候上个时间事件流还没有结束),又会产生一个新的时间事件流,这个新的时间事件流不仅取代了click事件流,还取代了第一次点击按钮产生的那个时间事件流。谨记switch永远会切换到最新的那个事件流。

内嵌observable引出了merge(),concat(),switch()各自的兄弟:mergeMap(),concatMap(),switchMap()。

mergeMap

还用上面的例子,把map改成mergeMap,去掉switch,区别是,click事件流同样被取代,但第一次点击产生的时间事件流不会被第二次点击的时间事件流取代,而是合并成了一个流(无序)。

switchMap

这个操作符完成的事儿实际就是上面例子中的map+switch。

concatMap

用concatMap替换map,去掉switch,点击三次按钮,我们会看到控制台输出三次0到4,前一次不结束,后面的一直等待。这里给大家一个赞赏我的机会,请用三个鼠标事件流+concatMap操作符+takeUntil操作符完成拖放页面元素的功能。你会发现,哇~好简单好明了。

takeUntil操作符接收一个observable为参数,含义是,接收上游事件并让它通过,直到参数observable开始发送事件。

其实更直观的感受这些操作符的强大之处,或者说Rx的强大之处,应该用ajax、promise这些更贴近日常开发的例子,譬如说之前提到过的搜索框提示,或者监控股票价格,气象温度等等,就留个各位自己尝试吧,实践出真知嘛。

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

推荐阅读更多精彩内容