订阅前已解析为可观察值

时间:2019-06-20 13:02:54

标签: angular typescript asynchronous observable

我有一个服务(data.service.ts)和一个组件(page.component.ts)。

在此data.service.ts服务中,有一个名为changes$的可观察对象

当我加载页面时,它成功地从存储中获取了数据并将其解析(使用.next())到可观察对象。

在我的page.component.ts组件中,它已订阅此可观察的内容,以下每一项更改均得到处理,但第一个(页面加载时)没有得到处理。

为什么?我的data.service.ts在将page.component.ts订阅到此changes$可观察对象之前,解析了数据(使用.next())。

“等待”直到订阅该组件的最佳方法是什么?

我找到了使用setTimeout(() => { ... }, 0)的解决方法,但感觉不对(如果您知道我的意思)

以下是一些代码:

data.service.ts:

export class DoorService implements OnDestroy {
    private data: DoorServiceData;

    private destroy$: Subject<void> = new Subject();
    private changesSubject$: Subject<IDoorSettings> = new Subject();
    // this way users cannot parse values to the subject. read-only...
    public changes$: Observable<IDoorSettings> = this.changesSubject$.asObservable().pipe(takeUntil(this.destroy$));

    constructor(){
        this.init();
    }


    // service initializer, set data etc. 
    private init() {
        setTimeout(() => {
            let savedData: DoorServiceData = this.getFromStorage();
            if(!savedData)
            {
                savedData =  {settings: {usedInStore: false, isValid: false}};
            }
            this.data = savedData;
            this.changesSubject$.next(this.data.settings);
            console.log("DOOR SERVICE DATA SET");
        }, 0)
    }
}

page.component.ts:

export class SearchComponent implements OnInit{
    public settings: IDoorSettings;

    constructor(public doorService: DoorService) {}

    ngOnInit(): void {
        this.watchSettings();
    }

    private watchSettings() {
        console.log("Watching settings");
        this.doorService.changes$.subscribe((val) => {
            this.settings = val;
        });
    }
}

使用chrome控制台,我可以告诉它在订阅组件之前解析数据(当我不使用“ setTimeout”解决方法时

处理这种情况的最佳方法是什么?

我也尝试过async管道,但是没有运气。

谢谢。

2 个答案:

答案 0 :(得分:2)

您应该代替BehaviourSubject或ReplaySubject。

  

“等待”直到订阅该组件的最佳方法是什么?

先生,在RxJS世界中,这是一个令人恐惧的想法。就像广播电台应该等待播放歌曲,直到至少有一个收听者收听为止。

以广播电台为例,您要告诉我的是,一旦收听,第一首歌曲就消失了,因此您添加了BehaviourSubject或ReplaySubject。

BehaviorSubject具有存储“当前”值的特征。这意味着您始终可以直接从BehaviorSubject获得最后发出的值。因此,一旦您收听广播,就会播放最后播放的歌曲。

const subject = new Rx.BehaviorSubject(Math.random());

// subscriber 1
subject.subscribe((data) => {
    console.log('Subscriber A:', data);
});

// output
// Subscriber A: 0.24957144215097515

ReplaySubject与BehaviorSubject在可向新订户发送“旧”值的方式上具有可比性。但是,它还有一个额外的特性,它可以记录一部分可观察到的执行,因此可以存储多个旧值并将它们“重播”给新的订户。 创建ReplaySubject时,您可以指定要存储多少值以及要存储多长时间。换句话说,您可以指定:“我要存储最近2个值,这些值是在新订阅之前的最近100毫秒中执行的。”

const subject = new Rx.ReplaySubject(2, 100);

// subscriber 1
subject.subscribe((data) => {
    console.log('Subscriber A:', data);
});

setInterval(() => subject.next(Math.random()), 200);

// subscriber 2
setTimeout(() => {
  subject.subscribe((data) => {
    console.log('Subscriber B:', data);
  });
}, 1000)

// Subscriber A: 0.44524184251927656
// Subscriber A: 0.5802631630066313
// Subscriber A: 0.9792165506699135
// Subscriber A: 0.3239616040117268
// Subscriber A: 0.6845077617520203
// Subscriber B: 0.6845077617520203
// Subscriber A: 0.41269171141525707
// Subscriber B: 0.41269171141525707
// Subscriber A: 0.8211466186035139
// Subscriber B: 0.8211466186035139

答案 1 :(得分:0)

尝试RepleySubject而不是Subject

 private changesSubject$: ReplaySubject<IDoorSettings> = new ReplaySubject(1);