---
title: "Subject、Observable和Observer"
date: "2026-04"
category: "Angular"
tags: ["Angular"]
description: "Observable的pipe那里说到了供水，但那边只是被动的供水——如果我想要中间往里面加水并让所有吃水者都喝上新的水呢？比如从地下水换成山泉水，带有甘甜的滋味（嗯......）..."
source: "https://enriquemark.com/zh-hans/posts/Subject%E3%80%81Observable%E5%92%8CObserver"
---

[Observable的pipe](/zh-hans/posts/Observable的pipe)那里说到了供水，但那边只是被动的供水——如果我想要中间往里面加水并让所有吃水者都喝上新的水呢？比如从地下水换成山泉水，带有甘甜的滋味（嗯......）。这里就得用`Subject`。不过这个地方我得稍微分出来说个东西，那就是`Observable`和`Observer`，这俩在用的时候都会有一个`next`出来，很容易搞混。`next`即操作水塔，同时也代表去水塔接水，也就是发布方定义传出数据和订阅放接受数据。

```ts
// 指定範型 <number>，表示這個流會發送數字
const observable = new Observable((subscriber: Subscriber<number>) => {
  subscriber.next(1); // 這裡的 next 是 Subscriber 實作的方法
  subscriber.complete();
});

// 訂閱測試
observable.subscribe({
  next: (val) => console.log(val),
  complete: () => console.log('Done')
});
```

`Subscriber<T>`继承了`Observer<T>`这个接口，而后者约定了必须要实现`next`、`error`和`complete`三个方法：

```TS
interface Observer<T> {
  next: (value: T) => void;
  error: (err: any) => void;
  complete: () => void;
}
```

而`next`的传入元素在定义`observable`的时候通过`subscriber.next(1)`直接给定了，这也是为什么下游直接`next: (val) => console.log(val)`能取到值的原因，它取到的正是这里的内容。这里搞明白了，我们就知道为啥`Subject`不同，因为它自身即是一个`Observable`，同时也是一个`Observer`。

```ts
// 1. 建一個水塔，默認是地下水
const mySubject = new Subject<string>();
// 2. A 跑來接水
mySubject.subscribe(data => console.log('A 收到:', data));

// 3. 把山泉水接入進水塔！
mySubject.next('山泉水'); 
// 主控台印出: A 收到: 山泉水
```

这就意味著，此对象的操作不再只是单纯的取水，还可以决定往里面加什么水。只要加了，后续所有的消费者，都会受到影响。所以实务上一般都不太会把`Subject`直接暴露出来，否则不可控情况下，下游会把整个数据流改的面目全非。

不过`Subject`具备一个「多播」 (Multicast)的特性，会导致其发布的时候回调全部下游订阅者（执行了`subscribe`方法的对象）的`next`，并把`mySubject.next('山泉水')`这个数据塞过去。也就是说，如果存在此时还未被触发`subscribe`的对象，它不仅拿不到这个值，其他值也拿不到。就跟一个高压广播水管一样，来了就全部强行喷出，之后就关闸，一滴水也不给你。这会导致的显著问题就可以见以下场景：事件触发机制下的操作，使用者点击之后才会去`subscribe`。那此时晚来者就会两手空空，什么都拿不到。

`BehaviorSubject`这时候就派上用场，它是一个持续作用的`Subject`，不仅会在第一轮塞给所有人山泉水，还会在后面有人来的时候也给他一份，可以说是一个「热情的给水人」，不管什么事来，你要了他就给，跟`Subject`那样过期不候的高冷性格相对。实际使用的时候，大部分也都是用这个。

但问题来了，若有人就是嘴欠，嫌弃山泉水不行，非要喝地下水怎么办？当前的供水单一，只会让下游取到山泉水——`ReplaySubject`就派上用场了。如果说前两者，一个是无状态的：`Subject`晚来了两手空空；一个是只有最新状态：`BehaviorSubject`无论什么时候来都给你最新的（山泉水）；`ReplaySubject`则是包含了过去和现在指定范围内的全部状态。它可以让下游消费者取到过去的N个资料，即让后续的取水者无论山泉水还是地下水（取决调用时指定的范围），都能拿到手。如果你真的就喜欢喝地下水，自己把山泉水丢弃即可，或者你都想要，全部喝了也无所谓。总之就是将处置权交给下游，水塔只管供水，不管你怎么处置。
