RxJS 面试题

2025年3月17日 | 阅读16分钟

关于 RxJS 和响应式编程最常被问到的面试题。

1) 什么是 RxJS?/ 你如何理解 RxJS?

RxJS 是 Reactive Extension for Javascript 的缩写。它是一个 JavaScript 库,使用 observables 来处理响应式编程,并处理异步数据调用、回调和基于事件的程序。RxJS 将“响应式编程”的概念引入到 Web 中。它为 TypeScriptJavaScript 实现了响应式扩展。

RxJS 的工作方式是观察者模式、迭代器模式和函数式编程的结合。

RxJS 是一个用于响应式编程的库,它使用 Observables 来简化异步或基于回调的代码的组合。它是一个独立的 JavaScript 库,让程序员能够访问 Observable。

RxJS 也可以与其他 JavaScript 库和框架一起使用。JavaScript 和 TypeScript 都很好地支持它。


2) 什么是响应式编程?

响应式编程是一种声明式编程范式,它处理异步数据流。响应式编程最初由 Glenn Wadden 于 1986 年作为监控控制与数据采集(SCADA)行业的一种编程语言开发。事件总线或典型的点击事件被称为异步事件流,在响应式编程中用于观察并产生一些副作用。响应式编程使我们能够创建任何事物的数据流,而不仅仅是来自点击和悬停事件。


3) 在学习 RxJS 之前我们需要了解什么?

在学习 RxJS 之前,我们必须具备 JavaScript、JavaScript 框架和 Angular 的基础知识。如果你对 JS 有基本的了解,你就能轻松理解这项技术。


4) RxJS 最突出的特点是什么?

以下是 RxJS 用于处理 RxJS 概念或响应式编程的一些最重要特性的列表。

观察者模式

Observer 是一个具有 next()、error() 和 complete() 方法的对象,当我们必须与 observable(即可观察对象)交互时,即源交互(例如按钮点击、Http 请求等)时会调用这些方法。

Observable

在 RxJS 中,observable 函数用于创建一个 observer(观察者),并将其附加到期望值的源上。例如,来自 DOM 元素的点击、鼠标事件或 Http 请求等。

认购

当 observable 被创建时,subscription(订阅)的作用就体现出来了。要执行 observable,我们需要订阅它。它也可以用来取消执行。

运算符

Operators(操作符)是 RxJS 中非常重要的一部分。操作符是一个纯函数,它接收一个 observable 输入,并以输出形式发出结果。输入和输出都是 observable。

主题

Subject(主题)是一种可以多播的 observable,即可以与多个观察者通信。假设我们有一个带事件监听器的按钮。每次用户点击按钮时,使用 addlistener 附加到事件的函数都会被调用。Subject 的功能也类似。

Schedulers(调度器)

scheduler(调度器)控制订阅何时开始和何时被通知的执行。


5) 响应式编程最大的优点是什么?

使用响应式编程的优点

  • 响应式编程提供了许多可以简化我们工作的操作符。
  • 响应式编程非常简单地组合数据流。
  • 它可以用来避免“回调地狱问题”。
  • 在响应式编程中,执行异步和多线程任务非常简单。
  • 它使复杂的线程处理变得非常容易。
  • 通过使用响应式编程,我们可以获得更清晰、更易读的代码库。
  • 在响应式编程中,实现背压(back-pressure)很容易。

6) 使用 RxJS 的最大优缺点是什么?

使用 RxJS 的优点

以下是使用 RxJS 的主要优点列表

  • RxJS可以与其他Javascript库和框架一起使用。它被javascript和typescript支持。一些例子是Angular, ReactJS, Vuejs, nodejs等。
  • 在处理异步任务方面,RxJS 是一个很棒的库。RxJS 使用 observables 来处理响应式编程,处理异步数据调用、回调和基于事件的程序。
  • RxJS 提供了大量的操作符,涵盖数学、转换、过滤、工具、条件、错误处理、连接等类别,与响应式编程结合使用时,可以极大地简化工作。

使用 RxJS 的缺点

以下是使用 RxJS 的最大缺点列表

  • 使用 observables 调试代码有点困难。
  • 当你开始使用 Observables 时,你最终可能会让你的全部代码都被 observables 包裹起来。

7) 什么是 Redux?

Redux 是一个用于管理应用程序状态的开源 JavaScript 库。它最常与 React、Angular 或 RxJS 等库一起用于构建用户界面。Redux 的灵感来自 Facebook 的 Flux 架构,并且与之非常相似。它由 Dan Abramov 和 Andrew Clark 创建。


8) Redux 的核心原则是什么?

Redux 遵循以下三个基本原则

  1. 单一数据源:它将整个应用程序的状态存储在单个 store 内的对象树中。单一状态树使得跟踪随时间的变化以及调试或检查应用程序变得更加容易。
  2. 状态是只读的:改变状态的唯一方法是发出一个 action。它确保并指定视图和网络回调都不会直接写入状态。
  3. 使用纯函数进行更改:我们必须编写纯 reducers 来指定状态树如何被 actions 转换。Reducers 是简单的纯函数,它接收先前的状态和一个 action,并返回下一个状态。

9) 你如何理解 RxJS Stream?

RxJS 流是按时间排序的持续事件序列。换句话说,我们可以说流是随时间可用的数据元素序列。流可以被看作是传送带上的物品,一次处理一个,而不是大批量处理。它被称为流,因为它像连续的数据一样,没有真正的终点,除非你明确定义一个终点。

一个流可以发出三种不同的东西

  • 一个值(某种类型)
  • 一个错误
  • 一个“完成”信号

10) 你如何理解《响应式宣言》这份文件?

一份被引入用以定义响应式编程核心原则的文件。这份文件被称为《响应式宣言》。《响应式宣言》于2013年由一个由Jonas Boner领导的开发者团队首次发布。《响应式宣言》支撑着响应式编程的原则。


11) Redux 和 RxJS 之间有相似之处吗?

尽管 Redux 和 RxJS 是用于非常不同目的的非常不同的库,但它们也有一些相似之处。

ReduxRxJS
Redux 是一个用于管理整个应用程序状态的工具。它通常用作 UI 的架构。它可以作为(一半)Angular 的替代品。RxJS 是一个响应式编程库。它通常被用作在 JavaScript 中完成异步任务的工具。可以把它看作是 Promises 的替代品。
Redux 有一点使用了响应式范式,因为 Store 是响应式的。Store 从远处观察 actions,并改变自身。RxJS 也使用响应式范式,但它不是一种架构,而是为你提供了基本的构建块,即 Observables,以实现这种“从远处观察”的模式。

12) 响应式编程和命令式编程有什么区别?

在响应式编程中,observables 发出数据,并将其发送给订阅者。这个过程可以被称为响应式编程中的数据被 PUSH(推送)。另一方面,在命令式编程中,数据是 PULL(拉取)的,我们明确地请求数据(遍历集合,从数据库请求数据等)。


13) RxJS 中的 BehaviorSubject 和 Observable 有什么区别?

下表说明了 RxJS 中 BehaviourSubject 和 Observable 的区别。

ObservableBehaviourSubject
在 RxJS 中,observable 是无状态的。在 RxJS 中,BehaviourSubject 是有状态的。
Observable 创建数据的副本。BehaviourSubject 共享数据。
Observable 本质上是单向的。BehaviourSubject 本质上是双向的。

14) RxJS 中的操作符是什么?RxJS 中使用了哪些不同类型的操作符?

操作符是 RxJS 中非常重要的一部分。一个 RxJS 操作符是一个纯函数,它接收一个 observable 作为输入,并以 observable 的形式提供输出。我们必须使用 pipe() 方法来使用操作符。

以下是 RxJS 中最常用的操作符列表

此外,这些操作符还被细分为其他类型的操作符。


15) RxJS 中的 Observable 是什么?

在 RxJS 中,observable 是一个函数,用于创建一个 observer 并将其附加到期望从中获取值的源上。例如,来自 DOM 元素的点击、鼠标事件或 Http 请求等,都是 RxJS observable 的例子。Observable 给了我们一个可调用的未来值或事件集合的概念。它使我们能够使用 observable 序列(也简称为 observables)来创建异步数据流。

我们必须遵循以下三个步骤来完成 RxJS observable

  • 创建 Observable
  • 订阅 Observable
  • 执行 Observable

16) RxJS Observables 和 Promises 有什么区别?

以下是 RxJS Observables 和 Promises 之间的主要区别列表

RxJS 可观察对象保证
Observables 用于异步运行,我们会多次获得返回值。Promises 用于异步运行,我们只获得一次返回值。
Observables 是惰性的(lazy)。Promises 不是惰性的。
Observables 可以被取消。Promises 不能被取消。
Observables 提供多个未来值。Promises 提供单个未来值。

17) RXJS Observables 相对于 RxJS Promises 有哪些优势?

在 RxJS 中,Observable 相对于 Promises 有许多优势。下面的列表展示了其中的一些

  • 一个 observable 就像一个流。它用于传递零个或多个事件,其中回调函数会为每个事件被调用。
  • Observable 总是优于 Promise,因为它提供了 Promise 的所有功能以及更多。
  • 通过使用 observable,我们可以处理单个或多个事件。
  • Observables 是可以取消的,所以这也是相对于 Promises 的一个优势。
  • Observable 允许延迟初始化。
  • Observable 允许格式化数据。
  • Observable 提供了像 map, forEach, reduce 等操作符。

18) React 和 RxJS 有什么区别?

React 是一个开源的 JavaScript 库,用于为以超文本标记语言呈现的数据提供视图。另一方面,RxJS 代表 Reactive Extensions for JavaScript。它被认为是一个用于在 Web 开发中组合异步编程和使用可观察序列及 LINQ 风格查询操作符的事件驱动程序的库。让我们看看它们之间的主要区别。

React 和 RxJS 的区别

ReactRxJS
React 是一个开源的 JavaScript 库。RxJS 是一个用于组合异步编程的库。
通过使用 React,我们可以轻松创建交互式 UI。通过使用 React,我们可以轻松地创建/组合异步或基于回调的代码。

19) 你如何理解 RxJS 中的“非阻塞”这个术语?

在 RxJS 或响应式编程中,如果竞争资源的线程的执行不会因为保护该资源的互斥而无限期地推迟,那么该算法就被称为非阻塞的。

这个概念用于一个 API,该 API 允许在资源可用时访问它;否则,它会立即返回,通知调用者资源当前不可用,或者操作已经启动但尚未完成。一个非阻塞的资源 API 允许调用者做其他工作,而不是被阻塞等待资源变得可用。这可以通过允许客户端注册以在资源可用或操作完成时获得通知来补充。


20) 在 RxJS 或响应式编程的背景下,“异步”是什么意思?

根据《牛津词典》,异步(asynchronous)一词可以定义为“不在同一时间存在或发生”。在响应式编程的上下文中,它意味着请求的处理发生在任意时间点,即在从客户端传输到服务之后的某个时间。客户端不能直接观察或同步服务内部发生的执行。异步是同步处理的反义词,同步处理意味着客户端只有在服务处理完请求后才恢复其执行。


21) RxJS 中的冷 Observable 和热 Observable 有什么区别?

简单来说,冷和热 Observable 的概念可以定义如下

当数据由 Observable 自身产生时,它被称为冷 Observable。当数据在 Observable 外部产生时,它被称为热 Observable。

让我们看看冷 Observables 和热 Observables 之间的区别

冷 Observables热 Observables
当数据在 Observable 内部产生时,我们可以称之为“冷” Observable。当数据在 Observable 外部产生时,我们称之为“热” Observable。
冷 observables 在订阅时开始运行。热 observables 甚至在订阅之前就已经产生值。
冷 observable 序列只有在调用 subscribe 时才开始向观察者推送值。像鼠标移动事件、股票选择器或 WebSocket 连接这样的热 observables 在订阅激活之前就已经在产生值了。
冷 Observable 在订阅时开始运行。热 Observable 在订阅之前产生值。
冷 Observable 序列开始推送值。在冷 Observable 中,数据生产者在 Observable 外部。
在冷 Observable 中,数据是在 Observable 内部产生的,所以我们不能在多个订阅者之间共享数据。两个或多或少同时订阅的 Observable 可能会收到两个不同的值。我们称这种行为为“单播(unicasting)”。正如我们所知,在热 Observable 中,数据是在 Observable 外部产生的,所以它可以在多个订阅者之间共享数据。这种行为是“多播(multicasting)”。

22) 你如何理解 RxJS 中的 Actor 模型?

一个 actor 模型可以做以下事情

  • Actor 模型指定您的并发原语是 actor。
  • 它可以向它知道的任何 actor 发送消息。
  • 它可以接收一条消息,并根据消息内容决定下一步做什么。
  • 它可以创建新的 actor 并提供某些保证,例如任何 actor 在同一时间只会处理一条消息,并且由 actor X 发送给 actor Y 的消息将按照它们被发送的顺序到达。

23) RxJS 中的 subject 是做什么的?

RxJS subject 是一种特殊类型的 observable,它允许将值多播给许多观察者。RxJS subject 是多播的,而普通的 observables 是单播的。subject 相当于一个事件发射器,是将一个值或事件多播给多个观察者的唯一方式。Subject 实现了 observable 和 observer 接口。每个 subject 都是一个 observable,所以你可以订阅它。每个 subject 都是一个 observer。这意味着你有 next、error 和 complete 方法,所以你可以发送值、错误 subject 或完成 subject。

Subject 的类型

  • 主题
  • ReplaySubject
  • BehaviorSubject
  • AsyncSubject

24) RxJS 中的 Subject、BehaviorSubject 和 ReplaySubject 有什么区别?

主题

在 RxJS Subject 中,后来订阅的观察者不会获得在他们订阅之前发出的数据值。

ReplaySubject

在 RxJS ReplaySubject 中,稍后订阅的观察者会收到在他们订阅之前发出的数据值。它的工作原理是使用一个缓冲区来保存发出的值,并在新观察者订阅时重新发出它们。

BehaviorSubject

BehaviorSubject 的功能类似于 ReplaySubject,但只重新发出最后发出的值。因此,当你对观察者的最后/当前值感兴趣时,应该使用它。


25) 什么是 RxJS Map,你如何理解高阶 Observable 映射?

RxJS map 操作符方便我们将 Observable 的有效负载投影到其他东西上。当我们开始使用 Rx 操作符来转换、组合、操作和处理由 Observables 发出的项目序列时,我们可以看到 Observables 的强大功能。

RxJS 高阶 Observable 映射

在高阶映射中,我们将源 observable 发出的值映射到另一个 Observable,而不是像将 1 这样的扁平值映射到像 10 这样的另一个值。结果是一个高阶 Observable。


26) 我们应该在 RxJS 中何时使用 switchMap、mergeMap 和 concatMap?

在 RxJS 中主要有四种类型的映射操作符:concatMap()、mergeMap()、switchMap() 和 exhaustMap()。所有这些操作符都是用于扁平化 observables 的映射或扁平化操作符,但它们适用于非常不同的场景。switchMap 和 mergeMap 是最强大和最常用的操作符。让我们看看我们何时使用这些操作符

concatMap() 操作符

以下是 concatMap() 操作符的示例代码

使用 concatMap() 操作符的两个主要好处是,我们不再需要使用带有高阶映射操作符的嵌套订阅,其次,所有 http 请求都按顺序发送到后端。

这就是 concatMap 操作符如何确保请求仍然按顺序发生的方式

  • concatMap 接收每个表单值,并将其转换为一个 observer HTTP,称为内部 observer。
  • concatMap 订阅内部 Observable 并将其输出发送到结果 Observable。
  • 第二个表单值的到来可能比在后端请求前一个表单值所需的时间更快。当这种情况发生时,新的表单值不会立即转换为 HTTP 请求。

mergeMap() 操作符

与 RxJS concatMap 操作符不同,mergeMap() 不会等到上一个 Observable 完成后才订阅下一个 Observable。

这是 mergeMap 操作符的工作方式

  • 在 mergeMap 操作符中,每个 Observable 源值都被映射到一个内部 Observable 中。
  • 然后内部 Observable 被 mergeMap 订阅。
  • 当内部 observables 发出新值时,输出 Observable 会立即反映它们。
  • 在 mergeMap 中,与 concatMap 操作符不同,我们不需要等到前一个内部 observable 完成。

switchMap() 操作符

与 mergeMap 操作符不同,在 switchMap 操作符中,如果新的 Observable 开始发出值,我们在订阅新的 Observable 之前会取消订阅前一个 Observable。


27) 什么是响应式编程中的背压(Back-Pressure)?

根据维基百科的定义,背压(Back-Pressure)是抵抗或反对流体通过管道的期望流动的力。但这一定义属于流体动力学。在软件的上下文中,定义将变为软件内的数据流,而不是通过管道的流体。所以,定义将是——

背压是一种抵抗或反对数据通过软件的期望流动的力。

当一个组件难以跟上时,整个系统需要做出明智的响应。处于压力下的组件以不受控制的方式失败或丢弃消息是不可接受的。由于它不容易处理且不能失败,它应该向上游组件传达它正处于压力之下,并让它们减少负载。这种背压是一个重要的反馈机制,它使系统能够优雅地响应负载而不是在这种情况下崩溃。背压可能会向上级联到用户,此时响应性可能会下降。但是,这种机制将确保系统在负载下具有弹性,并将提供信息,可能允许系统应用其他资源通过分配负载来减轻负载。

简单来说,我们可以说背压提供了应对 Observables 产生项目速度快于其观察者消费速度的策略。


28) 你如何理解与可伸缩性相对的弹性?

在IT基础设施中,“弹性(Elasticity)”一词可以定义为在不干扰基础设施的稳定性、性能、安全性、治理或合规性协议的情况下,快速扩展或削减容量和服务的能力。

这意味着系统的吞吐量会随着资源的按比例增加或减少而自动上下伸缩,以满足不断变化的需求。系统需要具有可伸缩性,才能从运行时动态添加或移除资源中受益。因此,弹性建立在可伸缩性的基础上,并通过增加自动资源管理的概念对其进行了扩展。


29) 失败(Failure)和错误(Error)有什么区别?

失败Error
失败(failure)可以定义为服务内部的意外事件,阻止其正常运行。当发生失败时,它通常会阻止对当前以及可能所有后续客户端请求的响应。错误(error)与失败不同。错误是一种常见情况,可能在输入验证期间出现,并将作为消息正常处理的一部分传达给客户端。
失败是意料之外的,它们需要干预,系统才能恢复到与之前相同的操作水平。错误是正常操作中预期的一部分。我们可以立即处理错误,系统在出现错误后将继续以相同的能力运行。
这并不意味着失败总是致命的。相反,在失败之后,系统的某些能力会降低。错误不是致命的。它们是编程的一部分,随时可能发生。

30) 命令式、函数式和响应式编程有什么区别?

让我们比较一下它们来看看区别

命令式编程:命令式编程是一种编程范式,其中每一行代码都按顺序执行以产生期望的结果。这种编程范式迫使程序员编写程序将“如何”解决某个任务。

函数式编程:函数式编程是一种编程范式,我们可以将所有东西都设置为函数的结果,从而避免改变状态和突变数据。

响应式编程:响应式编程是一种处理异步数据流或事件流的编程范式。事件流可以是任何东西,如键盘输入、按钮点击、手势、GPS 位置更新、加速度计、iBeacon 等。在这里,我们可以监听一个流并根据情况对其做出反应。


31) 你如何理解一个响应式系统要具有弹性(Resilient)?

对于一个响应式系统来说,具有弹性意味着系统在遇到任何失败的可能性时都能保持响应。任何不具有弹性的系统在失败后都会变得无响应。弹性是通过复制、遏制、隔离和委托来实现的。失败被遏制在每个组件内部,将组件彼此隔离,从而确保系统的某些部分可以失败和恢复,而不会危及整个系统。