1️⃣ 冷 Observable 和 热 Observable 的区别?
📌 面试重点:考察对 RxJS 订阅行为的理解。
-
冷 Observable(Cold Observable):每次订阅都会重新执行,数据流是独立的。
示例(每次订阅都会重新触发of(1,2,3)):const cold$ = of(1, 2, 3); cold$.subscribe(console.log); // 输出 1, 2, 3 cold$.subscribe(console.log); // 重新执行,输出 1, 2, 3 -
热 Observable(Hot Observable):所有订阅者共享同一数据流,不会因新订阅而重新执行。
示例(interval(1000)是热的,所有订阅者共享同一计时器):import { interval } from 'rxjs'; import { share } from 'rxjs/operators'; const hot$ = interval(1000).pipe(share()); hot$.subscribe(x => console.log('订阅 1:', x)); setTimeout(() => { hot$.subscribe(x => console.log('订阅 2:', x)); }, 3000);result:
订阅 1: 0 订阅 1: 1 订阅 1: 2 订阅 1: 3 订阅 2: 3 👈 和订阅 1 同步收到值 订阅 1: 4 订阅 2: 4 订阅 1: 5 订阅 2: 5
📌 总结:
- 冷 Observable = 惰性,每次订阅都会重新执行。
- 热 Observable = 主动,所有订阅者共享同一数据流。
shareReplay()可以让冷变热,避免重复 API 请求。
2️⃣ switchMap 和 exhaustMap 的区别?
📌 面试重点:考察 RxJS 并发控制。
| 操作符 | 作用 | 适用场景 |
|---|---|---|
switchMap | 取消上一个订阅,切换到最新的流 | 搜索框请求(只关心最新输入) |
exhaustMap | 忽略新的订阅,直到当前流完成 | 表单提交(防止重复提交) |
示例(switchMap:取消之前的请求) :
fromEvent(input, 'input').pipe(
switchMap(event => http.get(`/search?q=${event.target.value}`))
).subscribe(console.log);
🚀 新输入时,旧请求会被取消,适用于搜索框。
示例(exhaustMap:防止重复点击提交) :
fromEvent(button, 'click').pipe(
exhaustMap(() => http.post('/submit'))
).subscribe(console.log);
📌 新点击会被忽略,直到当前请求完成,适用于防止表单重复提交。
3️⃣ 如何处理 RxJS 中的错误?
📌 面试重点:考察对 catchError 和 retry 的使用。
✅ 方法 1:catchError
作用:捕获错误并返回默认值,防止流中断。
http.get('/data').pipe(
catchError(err => {
console.error('请求失败:', err);
return of([]); // 返回默认值,防止流中断
})
).subscribe(console.log);
✅ 方法 2:retry(重试)
作用:请求失败时自动重试。
http.get('/data').pipe(
retry(3), // 失败后重试 3 次
catchError(err => of([])) // 仍然失败时返回默认值
).subscribe(console.log);
✅ 方法 3:retryWhen(自定义重试策略)
作用:比如指数退避(每次重试间隔增加)。
http.get('/data').pipe(
retryWhen(errors => errors.pipe(
delay(1000), // 1 秒后重试
take(3) // 最多重试 3 次
))
).subscribe(console.log);
📌 适用场景:网络不稳定时,自动延迟重试。
4️⃣ mergeMap 和 concatMap 的区别?
📌 面试重点:考察 RxJS 并发控制和执行顺序。
| 操作符 | 是否并发 | 是否按顺序执行 | 适用场景 |
|---|---|---|---|
mergeMap | ✅ 并发 | ❌ 无序 | 批量并发 API 请求 |
concatMap | ❌ 串行 | ✅ 顺序执行 | 批量上传、按顺序请求 |
示例(mergeMap:并发) :
from([1, 2, 3]).pipe(
mergeMap(id => http.get(`/user/${id}`))
).subscribe(console.log);
🚀 所有请求并行执行,适用于批量加载数据。
示例(concatMap:顺序执行) :
from([1, 2, 3]).pipe(
concatMap(id => http.get(`/user/${id}`))
).subscribe(console.log);
📌 按顺序请求,等待上一个完成再执行下一个,适用于批量上传。
5️⃣ 如何让多个 API 请求依赖执行?
这是 RxJS 面试中的高频题!面试官重点考察你是否理解 多个 API 请求之间的依赖顺序与并发控制,能否熟练使用:
switchMap(取消前一个)concatMap(按顺序执行)forkJoin(并发多个,全部完成后处理)
🔁 后一个请求依赖前一个请求的结果
👉 用 switchMap 或 concatMap
this.api.getUser().pipe(
switchMap(user => this.api.getOrders(user.id)) // 用前一个结果作为输入
).subscribe(orders => {
console.log('用户订单', orders);
});
switchMap: 新请求会取消旧的,适合实时输入、搜索框等。
✅ 什么时候用 concatMap?
如果请求不能被取消,例如保存数据(POST),你希望一个接一个地执行:
from([user1, user2, user3]).pipe(
concatMap(user => this.api.saveUser(user))
).subscribe();
🤝 场景 2:多个请求并发执行,等所有完成后再处理结果
👉 用 forkJoin
forkJoin({
user: this.api.getUser(),
settings: this.api.getUserSettings()
}).subscribe(({ user, settings }) => {
console.log(user, settings);
});
- 所有 observable 同时开始
- 所有都完成才会发出结果(一次性)
- 常用于加载多个独立数据源,比如页面初始化
mergeMap
fromEvent(button, 'click').pipe(
mergeMap(() => this.api.getData()) // 每次点击都发请求
).subscribe(res => console.log(res));
🔍 总结对比表:
| 操作符 | 特点 | 常用场景 |
|---|---|---|
switchMap | 取消旧的、只保留最新的 | 实时搜索、动态请求 |
concatMap | 严格按顺序,一个接一个 | 提交表单、按序保存 |
forkJoin | 所有请求并发,等全部完成 | 页面初始化加载 |
mergeMap | 并发执行多个 | 并发批量上传 |
exhaustMap | 忽略新的直到旧的完成 | 登录防重复点击 |
需要我做一个 StackBlitz 示例,演示以上三种场景的真实效果吗?🔍
6️⃣ RxJS 什么时候应该用 BehaviorSubject?
📌 面试重点:考察对 BehaviorSubject 的理解。
- 普通
Subject:新订阅者不会收到之前的值。 BehaviorSubject:新订阅者可以收到最新的值,适用于存储状态。
示例(BehaviorSubject 共享状态) :
const user$ = new BehaviorSubject(null);
// 任何地方订阅都能拿到最新值
user$.subscribe(user => console.log('订阅 1:', user));
user$.next({ id: 1, name: 'Amanda' });
user$.subscribe(user => console.log('订阅 2:', user)); // 能拿到 { id: 1, name: 'Amanda' }
📌 适用于:存储**用户信息、主题模式(dark/light)**等全局状态。
📌 总结
| 题目 | 重点 |
|---|---|
| 冷 vs. 热 Observable | 订阅行为:冷 Observable 重新执行,热 Observable 共享数据流 |
| switchMap vs. exhaustMap | switchMap 取消上一个,exhaustMap 忽略新请求(防止重复提交) |
| 错误处理 | catchError、retry、retryWhen |
| mergeMap vs. concatMap | mergeMap 并行,concatMap 串行(适用于上传任务) |
| API 依赖执行 | switchMap(依赖请求),forkJoin(并行等全部完成) |
| BehaviorSubject | 存储状态,新订阅者能拿到最新值 |