RxJs map operator 工作原理分析

使用一个例子来研究 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 1
    mapTo(1),
    // keep a running total
    scan((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), 在这里得到执行:

(0)

相关推荐

  • Python|利用map函数解决竞赛模拟题

    问题描述将整数数组中是6的倍数的元素按照输入次序依次输出.如果没有符合条件的元素则输出-1.第一行是整数N(N<10000),表示数组中的元素个数,第二行是这个数组中的N个元素,输出数为组序列中 ...

  • 「Flink」Flink 1.9 WebUI运行作业界面分析

    运行作业界面 在以下界面中,可以查看到作业的名称.作业的启动时间.作业总计运行时长.作业一共有多少个任务.当前正在运行多少个任务.以及作业的当前状态. 这里的程序:一共有17个任务,当前正在运行的是1 ...

  • RxJs fromEvent 工作原理分析

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

  • 24V开关电源电路构成几工作原理分析

    电路以UC3842振荡芯片为,构成逆变.整流电路.UC3842一种高性能单端输出式电流控制型脉宽调制器芯片,相关引脚功能及内部电路原理已有介绍,此处从略.AC220V电源经共模滤波器L1引入,能较好抑 ...

  • 开关电源工作原理分析及图解

    描述 开关电源就是用通过电路控制开关管进行高速的导通与截止. 将直流电转化为高频率的交流电提供给变压器进行变压,从而产生所需要的一组或多组电压!转为高频交流电的原因是高频交流在变压器变压电路中的效率要 ...

  • BUCK电路工作原理分析详细阐述

    在电子电路中,电源一般分为两类,一类是线性电源,一类是开关电源.线性电源具有噪声小的优点.开关电源虽然噪大,但是具有效率高.热损小的优点. 开关电源还可以细分为降压型.升压型和升降压三类.也可按照隔离 ...

  • 8K视频指的什么?8K视频处理和工作原理分析

    随着视频技术的不断发展,分辨率从480P发展到1080P;当我们还没有完全意识到4K电视将一统天下的时候, 8K的直播就已开始.8K视频要求需要处理每帧约33M像素的数据量,海量的数据处理为目前的视频 ...

  • 海尔变频空调控制电路工作原理分析

    在控制电路板中应用了一片型号为CMC93-0057微处理器芯片,它是整个控制电路板的核心器件,海尔KFR35GW变频空调的运行控制信号都是由这个芯片发出的.CMC93-0057微处理器芯片主要引脚功能 ...

  • 直流变频空调的工作原理分析(图)

    前言:下图为变频空调变频部分的基本构成.电源220V交流电压经转换器变换为直流.逆变器主要功能为实现换向,把直流电压转换成任意频率的有效值相当于三相交流电的脉冲电压信号;其最常见的结构形式是六个半导体 ...

  • 485通信自动收发电路工作原理分析

    简单介绍 485接口电路

  • 三极管工作原理分析,精辟、透彻,看后你就懂

    说明:内容与之前那篇一样,由于之前那篇是转载百度的,现在图片受限,无法阅读.这篇自己添加了图片资源. 随着科学技的发展,电子技术的应用几乎渗透到了人们生产生活的方方面面.晶体三极管作为电子技术中一个最 ...