关于angular:BehaviorSubject vs Observable?

BehaviorSubject vs Observable?

我在研究角度rxjs模式,我不理解BehaviorSubjectObservable之间的区别。

据我所知,BehaviorSubject是一个可以随时间变化的值(可以订阅,订户可以接收更新的结果)。这似乎与Observable的目的完全相同。

你什么时候使用ObservableBehaviorSubject?使用BehaviorSubject比使用Observable有好处吗?反之亦然?


行为主题是一种主题类型,主题是一种特殊的可观察类型,因此您可以像订阅任何其他可观察的消息一样订阅消息。行为主体的独特特征是:

  • 它需要一个初始值,因为它必须始终在订阅时返回一个值,即使它没有收到next()
  • 订阅后,返回主题的最后一个值。只有当它接收到一个onnext时,才触发一个常规的可观察的触发器。
  • 在任何时候,都可以使用getValue()方法在不可见代码中检索主题的最后一个值。

与观察对象相比,受试者的独特特征是:

  • 它是一个观察者,除了是一个可观察的,所以除了订阅它之外,您还可以向一个主题发送值。

此外,你可以使用BehaviorSubject上的asObservable()方法从行为主体中获得可观察的结果。

observable是一个通用的,BehaviorSubject在技术上是observable的一个子类型,因为behaviorssubject是一个具有特定性质的observable。

BehaviorSubject示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Behavior Subject

// a is an initial value. if there is a subscription
// after this, it would get"a" value immediately
let bSubject = new BehaviorSubject("a");

bSubject.next("b");

bSubject.subscribe(value => {
  console.log("Subscription got", value); // Subscription got b,
                                          // ^ This would not happen
                                          // for a generic observable
                                          // or generic subject by default
});

bSubject.next("c"); // Subscription got c
bSubject.next("d"); // Subscription got d

例2:常规主题:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Regular Subject

let subject = new Subject();

subject.next("b");

subject.subscribe(value => {
  console.log("Subscription got", value); // Subscription wont get
                                          // anything at this point
});

subject.next("c"); // Subscription got c
subject.next("d"); // Subscription got d

使用subject.asObservable()可以从SubjectBehaviorSubject中创建一个可观察的。

唯一的区别是,不能使用next()方法将值发送到可观测值。

在Angular服务中,我将使用BehaviorSubject作为数据服务,因为Angular服务通常在组件和行为主题初始化之前进行初始化,以确保使用该服务的组件接收最后更新的数据,即使组件订阅该数据后没有新的更新。


可观察:每个观察者的结果不同

一个非常重要的区别。因为Observable只是一个函数,它没有任何状态,所以对于每个新的观察者,它会一次又一次地执行Observable创建代码。这将导致:

The code is run for each observer
. If its a HTTP call, it gets called for each observer

这会导致严重的错误和效率低下

BehaviorSubject(或Subject)存储观察者详细信息,只运行一次代码,并将结果提供给所有观察者。

前任:

jsbin:http://jsbin.com/qowulet/edit?JS,控制台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// --- Observable ---
let randomNumGenerator1 = Rx.Observable.create(observer => {
   observer.next(Math.random());
});

let observer1 = randomNumGenerator1
      .subscribe(num => console.log('observer 1: '+ num));

let observer2 = randomNumGenerator1
      .subscribe(num => console.log('observer 2: '+ num));


// ------ BehaviorSubject/ Subject

let randomNumGenerator2 = new Rx.BehaviorSubject(0);
randomNumGenerator2.next(Math.random());

let observer1Subject = randomNumGenerator2
      .subscribe(num=> console.log('observer subject 1: '+ num));
     
let observer2Subject = randomNumGenerator2
      .subscribe(num=> console.log('observer subject 2: '+ num));

1
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.3/Rx.min.js">

输出:

1
2
3
4
"observer 1: 0.7184075243594013"
"observer 2: 0.41271850211336103"
"observer subject 1: 0.8034263165479893"
"observer subject 2: 0.8034263165479893"

观察使用Observable.create如何为每个观察者创建不同的输出,但BehaviorSubject为所有观察者提供相同的输出。这很重要。

总结其他差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         Observable                  ┃     BehaviorSubject/Subject         ┃      
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
│ Is just a function, no state        │ Has state. Stores data in memory    │
├─────────────────────────────────────┼─────────────────────────────────────┤
│ Code run for each observer          │ Same code run                       │
│                                     │ only once for all observers         │
├─────────────────────────────────────┼─────────────────────────────────────┤
│ Creates only Observable             │Can create and also listen Observable│
│ ( data producer alone )             │ ( data producer and consumer )      │
├─────────────────────────────────────┼─────────────────────────────────────┤
│ Usage: Simple Observable with only  │ Usage:                              │
│ one Obeserver.                      │ * Store data and modify frequently  │
│                                     │ * Multiple observers listen to data │
│                                     │ * Proxy between Observable  and     │
│                                     │   Observer                          │
└─────────────────────────────────────┴─────────────────────────────────────┘


Observable对象表示基于推送的集合。

观察者和可观察的接口为基于推送的通知提供了一种通用机制,也称为观察者设计模式。可观察对象表示发送通知的对象(提供者);观察对象表示接收通知的类(观察者)。

主体类继承了可观察和可观察两种类型,即它既是观察者又是可观察者。您可以使用主题订阅所有观察者,然后将主题订阅到后端数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var subject = new Rx.Subject();

var subscription = subject.subscribe(
    function (x) { console.log('onNext: ' + x); },
    function (e) { console.log('onError: ' + e.message); },
    function () { console.log('onCompleted'); });

subject.onNext(1);
// => onNext: 1

subject.onNext(2);
// => onNext: 2

subject.onCompleted();
// => onCompleted

subscription.dispose();

更多信息请访问https://github.com/reactive-extensions/rxjs/blob/master/doc/gettingstarted/subjects.md


可观测的和主体都是可观测的,这意味着观察者可以跟踪它们。但它们都有一些独特的特点。此外,共有3种类型的受试者,每种受试者都具有独特的特征。让我们试着理解每一个。

你可以在Stackblitz上找到一个实际的例子。(您需要检查控制台以查看实际输出)

enter image description here

埃多克斯1〔22〕

它们是冷的:代码在列表中执行,它们只有一个观察者。

创建数据副本:Observable为每个观察者创建数据副本。

单向:观察者不能将值赋给可观察的(原始/主)。

江户十一〔一〕号

它们是热的:代码被执行,值被广播,即使没有观察者。

共享数据:所有观察者共享相同的数据。

双向:观察者可以将值分配给可观察的(原始/主)。

如果使用的是"使用主题",那么在创建观察者之前,您会错过所有已浏览的值。重放主题来了

埃多克斯1〔24〕

它们是热的:代码被执行,值被广播,即使没有观察者。

共享数据:所有观察者共享相同的数据。

双向:观察者可以将值分配给可观察的(原始/主)。加

重播消息流:无论您何时订阅重播主题,都将收到所有brodcasted消息。

在Subject和Replay Subject中,不能将初始值设置为Observable。所以行为主体来了

埃多克斯1〔25〕

它们是热的:代码被执行,值被广播,即使没有观察者。

共享数据:所有观察者共享相同的数据。

双向:观察者可以将值分配给可观察的(原始/主)。加

重播消息流:无论您何时订阅重播主题,都将收到所有brodcasted消息。

您可以设置初始值:您可以用默认值初始化Observable。


我在示例中没有看到的一件事是,当您通过asobservable将BehaviorSubject强制转换为Observable时,它继承了在订阅时返回最后一个值的行为。

这是一个棘手的问题,因为库通常会公开可观察的字段(即Angular2中ActivateDroute中的参数),但可能会在幕后使用主题或行为主题。他们使用什么会影响订阅的行为。

请参见http://jsbin.com/ziquxapubo/edit?HTML、JS、控制台

1
2
3
4
5
6
7
8
9
10
11
let A = new Rx.Subject();
let B = new Rx.BehaviorSubject(0);

A.next(1);
B.next(1);

A.asObservable().subscribe(n => console.log('A', n));
B.asObservable().subscribe(n => console.log('B', n));

A.next(2);
B.next(2);

Observable只允许订阅,而Subject允许发布和订阅。

因此,主题允许您的服务同时用作发布服务器和订阅服务器。

到目前为止,我对Observable还不是很在行,所以我只举一个Subject的例子。

让我们用一个有角度的cli示例更好地理解它。运行以下命令:

1
2
3
4
5
6
7
npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

app.component.html的内容替换为:

1
2
3
4
5
  {{message}}



</app-home>

运行命令ng g c components/home生成主组件。将home.component.html的内容替换为:

1
2
<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)">Send message</button>

#message是这里的局部变量。在app.component.ts的类中添加属性message: string;

运行此命令ng g s service/message。这将在src\app\service\message.service.ts上生成一个服务。向应用程序提供此服务。

Subject导入MessageService中。也添加一个主题。最终代码应如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class MessageService {

  public message = new Subject<string>();

  setMessage(value: string) {
    this.message.next(value); //it is publishing this value to all the subscribers that have already subscribed to this message
  }
}

现在,在home.component.ts中注入此服务,并将其实例传递给构造函数。这也适用于app.component.ts。使用此服务实例将#message的值传递给服务函数setMessage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component } from '@angular/core';
import { MessageService } from '../../service/message.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent {

  constructor(public messageService:MessageService) { }

  setMessage(event) {
    console.log(event.value);
    this.messageService.setMessage(event.value);
  }
}

app.component.ts中,向Subject订阅和取消订阅(以防止内存泄漏):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { Component, OnDestroy } from '@angular/core';
import { MessageService } from './service/message.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {

  message: string;
  subscription: Subscription;

  constructor(public messageService: MessageService) { }

  ngOnInit() {
    this.subscription = this.messageService.message.subscribe(
      (message) => {
        this.message = message;
      }
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

就这样。

现在,任何输入到home.component.html#message中的值都应打印到app.component.html中的{{message}}中。


应用组件.ts

1
behaviourService.setName("behaviour");

行为.服务.ts

1
2
3
4
5
6
7
8
private name = new BehaviorSubject("");
getName = this.name.asObservable();`

constructor() {}

setName(data) {
  this.name.next(data);
}

自定义组件.ts

1
2
3
behaviourService.subscribe(response=>{
  console.log(response);    //output: behaviour
});


BehaviorSubject与Observable:RXJS有观察者和可观察者,RXJS提供了一个用于数据流的多个类,其中一个是BehaviorSubject。

观察:观察是一段时间内多个值的惰性集合。

BehaviorSubject:需要初始值并将其当前值发送给新订户的主题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 // RxJS v6+
import { BehaviorSubject } from 'rxjs';

const subject = new BehaviorSubject(123);

//two new subscribers will get initial value => output: 123, 123
subject.subscribe(console.log);
subject.subscribe(console.log);

//two subscribers will get new value => output: 456, 456
subject.next(456);

//new subscriber will get latest value (456) => output: 456
subject.subscribe(console.log);

//all three subscribers will get new value => output: 789, 789, 789
subject.next(789);

// output: 123, 123, 456, 456, 456, 789, 789, 789