Subject, BehaviorSubject, ReplaySubject, AsyncSubject

1. Subject

直接用subjectnext方法传值,所有订阅的observer就会接收到,因为Subject本身是Observable它会在内部管理一份observer的事件清单

const subject = new Subject();

subject.subscribe({
    next: value => console.log('next: ' + value),
    error: error => console.log('error: ' + error),
    complete: () => console.log('complete!')
});
//  next: 1
//  next: 2
//  next: 3 等待1s

subject.next(1);
subject.next(2);

setTimeout(() => {
    subject.next(3);
}, 1000);

//  这里只会收到`3`,因为只有next(3)是该订阅之后执行的
subject.subscribe({
    next: value => console.log('next: ' + value),
    error: error => console.log('error: ' + error),
    complete: () => console.log('complete!')
});
//  next: 3

2. BehaviorSubject

很多时候我们会希望Subject能代表当前的状态,而不是单纯的事件发送,也就是说如果有一个新的订阅,我们希望Subject能立即给出最新的值,而不是订阅后要等待执行next()才能收到新值

const subject = new Subject();

subject.subscribe({
    next: value => console.log('next: ' + value),
    error: error => console.log('error: ' + error),
    complete: () => console.log('complete!')
});
//  next: 1
//  next: 2

subject.next(1);
subject.next(2);

setTimeout(() => {
    //	这里不会收到任何消息,因为是在next()之后订阅的
    subject.subscribe({
        next: value => console.log('next: ' + value),
        error: error => console.log('error: ' + error),
        complete: () => console.log('complete!')
    });
}, 1000);

我们希望subject订阅时就能立即收到2,可以用BehaviorSubject

BehaviorSubject Subject最大的不同就是BehaviorSubject是用来呈现当前的值,而不是单纯的发送事件。BehaviorSubject会记住最新一次发送的元素,并把该元素当作目前的值,在使用上BehaviorSubject需要传入一个参数来代表默认的状态,示例如下:

const subject = new BehaviorSubject(0); //  0为默认值

subject.subscribe({
    next: value => console.log('next: ' + value),
    error: error => console.log('error: ' + error),
    complete: () => console.log('complete!')
});
//  next: 0
//  next: 100

subject.next(100);

setTimeout(() => {
    subject.subscribe({
        next: value => console.log('next: ' + value),
        error: error => console.log('error: ' + error),
        complete: () => console.log('complete!')
    });
    //  next: 100
}, 1000);

这个示例可以看得出来BehaviorSubject在创建时就需要给定一个状态,并在之后任何一次订阅,就会先送出最新的状态。其实这种行为就是一种状态而非单纯的事件。

3. ReplaySubject

在某些时候我们会希望Subject代表事件,但又能在新订阅时重新发送最后的几个元素,这时我们就可以用ReplaySubject。示例如下

const subject = new ReplaySubject(2); //  重新发送最后两个元素

subject.subscribe({
    next: value => console.log('next: ' + value),
    error: error => console.log('error: ' + error),
    complete: () => console.log('complete!')
});
//  next: 1
//  next: 2
//  next: 3

subject.next(1);
subject.next(2);
subject.next(3);

setTimeout(() => {
    subject.subscribe({
        next: value => console.log('next: ' + value),
        error: error => console.log('error: ' + error),
        complete: () => console.log('complete!')
    });
    //  next: 2
    //  next: 3
}, 1000);

可能会有人认为ReplaySubject(1)是不是就等同于BehaviorSubject,其实是不一样的,BehaviorSubject在建立时就会有起始值,比如BehaviorSubject(0)起始值就是0BehaviorSubject是代表着状态而ReplaySubject只是事件的重放而已。

4. AsyncSubject

AsyncSubject是最怪的一个变形,他有点像是operator last,会在subject结束后送出最后一个值。其实这个行为跟Promise很像,都是等到事情结束后送出最后一个值。示例如下

const subject = new AsyncSubject();

subject.subscribe({
    next: value => console.log('next: ' + value),
    error: error => console.log('error: ' + error),
    complete: () => console.log('complete!')
});
//  等待1s
//  next: 3
//  complete!

subject.next(1);
subject.next(2);
subject.next(3);

setTimeout(() => {
    subject.complete();
}, 1000);