使用一个例子来研究 map 操作符的工作原理。

推荐阅读本文之前,先浏览这篇文章RxJs fromEvent 工作原理分析以了解相关知识。

源代码:

import { Component, OnInit, Inject } from '@angular/core';
import { fromEvent, combineLatest } from 'rxjs';
import { mapTo, startWith, scan, tap, map } from 'rxjs/operators';
import { DOCUMENT } from '@angular/common';@Component({selector: 'app-combine-latest',templateUrl: './combine-latest.component.html'
})
export class CombineLatestComponent implements OnInit {readonly document: Document;constructor(// https://github.com/angular/angular/issues/20351@Inject(DOCUMENT) document: any) {this.document = document as Document;}redTotal:HTMLElement;blackTotal: HTMLElement;total:HTMLElement;  test:HTMLElement;ngOnInit(): void {this.redTotal = this.document.getElementById('red-total'); this.blackTotal = this.document.getElementById('black-total');this.total = this.document.getElementById('total');this.test = this.document.getElementById('test');combineLatest(this.addOneClick$('red'), this.addOneClick$('black')).subscribe(([red, black]: any) => {this.redTotal.innerHTML = red;this.blackTotal.innerHTML = black;this.total.innerHTML = red + black;});fromEvent(this.test, 'click').pipe(map( event => event.timeStamp)).subscribe((event) => console.log(event));}addOneClick$ = id =>fromEvent(this.document.getElementById(id), 'click').pipe(// map every click to 1mapTo(1),// keep a running totalscan((acc, curr) => acc + curr, 0),startWith(0));
}

打开页面,点击 Test 按钮,能在 Chrome 控制台里看到每次点击发生时的 timestamp 时间戳:

下面介绍 map 操作符是如何起作用的。

先缕一缕顺序:

  1. 首先执行fromEvent,返回一个 Observable 对象。

  2. 执行 map 操作符,其结果作为输入,传入 pipe

2.执行 pipe:

  1. 执行 subscribe 操作。

我们可以把 pipe 形象地想象成管道,通过 fromEvent 返回的 Observable 对象,流过一根根管道,最后触发其订阅者,执行订阅者的逻辑。那么 RxJs 提供的各种 operator,就是安装在管道里的处理器。

map 操作的输入是我们定义的映射函数,在 RxJs 上下文里,称为 project:

map 返回一个新的函数,名为 mapOperation. 新函数体里,基于传入的 project,创建一个新的 MapOperator. 这个 MapOperator,作为新函数输入参数 source 的 lift 方法调用的输入参数。到现在为止,我们尚且不知道 source 参数的类型。

接下来执行 Observable 的 pipe 方法。

operations 参数是 map operator 返回的新函数,mapOperation:

pipeFromArray 的实现,如果 pipe 输入只有一个 operator,这种情况比较简单,进入第 9 行的 IF 分支,直接将 map 返回的 mapOperation 函数作为 pipeFromArray 调用的返回结果。

注意到 Observable.js 实现里,在 pipeFromArray(operations) 返回之后,紧跟了另一个括号,说明这是另一个函数调用,输入参数为 this,即 Observable 对象本身。

现在进入到 map 操作返回的 新函数 mapOperation 的函数体内部了:

因为此时 button 尚未点击,因此 Observable 对象并没有 emit 值,只是完成相关的 setup 工作。

这行语句:

return source.lift(new MapOperator(project, thisArg));

只是返回一个新的 Observable 对象,其 source 属性指向调用 lift 操作的原始 Observable 对象,而 operator 属性指向 new MapOperator 返回的结果,后者是 project 的 wrapper.

如此一来,调用 subscribe 方法注册应用程序监听函数的 Observable 对象,再也不是 fromEvent 返回的原始 Observable 对象,而是前者调用了 pipe,接收了 map 指定的 project 之后,由 source.lift( new MapOperator) 返回的新 Observable 对象。

这个新的 Observable 对象,调用 subscribe 方法,执行逻辑和这篇文章RxJs fromEvent 工作原理分析介绍的相比有所差异,复杂度稍稍增加了。

把 Observable 对象 operator 属性值提取出来:

接下来的 21行代码执行,和之前没有 operator 时相比,没有差异,略过。

前一篇文章进入 ELSE 分支,而本文因为 operator 的存在,进入 22 行的 IF 分支:

首先执行 operator.call 方法:

MapSubscriber 也是 Subscriber 的子类之一,和其父类相比,多了 project 属性。

再次执行 subscribe:

因为这次传入的 Observable 是最原始的即 fromEvent 返回的 Observable,因此不存在 operator,所以进入 ELSE 分支执行:

重点分析 this 和 sink:

this 是 fromEvent 返回的原始 Observable,而 sink 是包含了 map operator 以及应用程序定义的订阅逻辑的 Subscriber:

_trySubscribe 调用 _subscribe:

最终仍旧进入了 fromEvent 的核心逻辑:

这段代码,定义了 fromEvent,以什么样的方式,emit 何种类型的数据。

  • 什么样的方式?addEventListener,每次 eventTarget 定义的 HTMLElement 发生 click 事件时,emit 数据
  • emit 的数据格式为 MouseEvent.

至此 Observable 相关的 setup 执行完毕。

点击按钮,触发之前通过 addListener 注册的 handler 函数。fromEvent.js 此处 subscriber 不是原始的 subscriber,而是 MapSubscriber,其 destination 属性的 _next, 指向了应用程序指定的订阅处理逻辑。Emit 的数据是 MouseEvent.

MapSubscriber 的特色:在将原始值 MouseEvent 交给应用程序之前,先要执行 project 对其进行处理:

这个 project 的逻辑是,将 MouseEvent 对象映射成 timestamp 时间戳:

将 project 处理结果返回给destination 继续进行传递:

this._next 指向的是应用程序定义的 console.log(event), 在这里得到执行:

更多Jerry的原创文章,尽在:“汪子熙”:

RxJs map operator 工作原理分析相关推荐

  1. Hadoop生态圈-Zookeeper的工作原理分析

    Hadoop生态圈-Zookeeper的工作原理分析 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   无论是是Kafka集群,还是producer和consumer都依赖于Zoo ...

  2. 原理剖析(第 009 篇)ReentrantReadWriteLock工作原理分析

    2019独角兽企业重金招聘Python工程师标准>>> 原理剖析(第 009 篇)ReentrantReadWriteLock工作原理分析 一.大致介绍 1.在前面章节了解了AQS和 ...

  3. 匿名突破网络限制 (Tor工作原理分析)

    转载自:http://blog.163.com/mike_gz/blog/static/247532200672932800/ 网管顾名思义,就是天天管着网络.想尽各种手段限制我们上网的人.在网络中订 ...

  4. 滑环的基本结构及工作原理分析

    滑环是工业机械部件,基本原理是利用导电机械部件的折动或滚动在接触旋转部件和静止部件之间传递电能或电信号的产业部部件,作为360度旋转方式的一个相当好的解决方法,那么,下面一起了解下滑环的基本结构及工作 ...

  5. 【高通SDM660平台 Android 10.0】(19) --- Camera_focus、Camera_snapshot、volume_up 按键工作原理分析

    [高通SDM660平台 Android 10.0]19 --- Camera_focus.Camera_snapshot.volume_up 按键工作原理分析 一. DTS代码配置 二. Kernel ...

  6. 二极管温度补偿电路工作原理分析

    众所周知,PN结导通后有一个约为0.6V(指硅材料PN结)的压降,同时PN结还有一个与温度相关的特性:PN结导通后的压降基本不变,但不是不变,PN结两端的压降随温度升高而略有下降,温度愈高其下降的量愈 ...

  7. 船型开关工作原理分析

    船型开关工作原理分析 由于疫情,这几个月一直无聊的待在家里,就把家里翻出来的旧电热锅简单的修理了一下,关于电热锅的修理博客之后有时间再写吧,这次就总结下在修过程中让我一直很头疼的船型开关的工作原理. ...

  8. 全自动升降柱工作原理分析

    全自动升降柱工作原理分析 全自动升降柱使用范围比较广泛,在城市交通安全中提供非常便捷的交通舒缓措施,可控制车辆行驶确保安全,全自动升降柱外观设计精美, 具有高的防撞击效果,当电动升降柱升起可在地面形成 ...

  9. RxJs fromEvent 工作原理分析

    fromEvent(this.test, 'click').subscribe((event) => console.log(event)); this.test 的赋值逻辑: this.tes ...

最新文章

  1. 一个TreeView 样式表
  2. 郑州计算机技校哪个学校好,郑州十大技校排名 详细介绍
  3. Py之imblearn:imblearn/imbalanced-learn库的简介、安装、使用方法之详细攻略
  4. PAT (Basic Level) Practice (中文)1015 德才论 (25 分)
  5. Spring MVC 4快速入门Maven原型已改进
  6. 【老杜】MySQL—day01
  7. 系统操作手册_东芝CT操作手册——系统概述
  8. 微软随.NET 4.5发布新REST API框架
  9. 两台计算机怎样共享一台打印机共享文件夹,两台电脑怎么共享文件和打印机
  10. 怎么看mmdetection版本_mmdetection使用指南
  11. 非主流图片制作,手机图片制作
  12. Linux修改fstab引起系统无法启动问题的解决方法
  13. 利用 画图 快速给图片添加文字
  14. 几何光学学习笔记(2)- 1.2 费马原理、马吕斯定律和成像
  15. 【iOS15更新必学】 如何完整备份iPhone资料?
  16. MySQL索引,检索数据库
  17. 《大数据实时处理》学生学期总结
  18. OA系统高性能部署方案
  19. office 2007 1706错误解决办法
  20. 第二届“中科实数杯”全国电子数据取证 wp

热门文章

  1. 文档加载完后执行相关事件
  2. HDU-1847 Good Luck in CET-4 Everybody! (博弈+找规律)
  3. 联想EDU同传系统 版本7.5 7.6在机房中出现的一些故障和解决办法
  4. org.tinygroup.databasebuinstaller-数据库结构及元数据自动创建
  5. 关于字符编码,你所需要知道的
  6. 使用Hexo搭建博客,备份至GitHub过程(基于网上资料的实践操作)
  7. JSP(五):属性范围
  8. VMware下安装CentOS7 无法通过桥接模式进行联网
  9. 人月神话阅读笔记 03
  10. ch=cin.get()和cin.get(ch)返回值问题