RXJS观察者相互减速

huangapple go评论70阅读模式
英文:

RXJS observers slow down each other

问题

I have the page with the documents list on the left (with clickable items, each item will select a document), and the actual content of the selected document in the right side, which takes up the bigger part of the page. The problem is that when a big document is clicked, it causes the delay to display it, but more importantly, the selected menu item (from EvahubSidenavComponent) is not displayed immediately, but it seems to be waiting for the document content (in EvahubDocumentsComponent) to be displayed first. Why? The document itself can be as much as 10MBs of text, so it's okay if it needs to be read-in over a second or two, but the menu item on the left (in the evahub-sidenav) should be colored immediately. I have these two crucial observations in the state store as so:

currentDocumentId$: Observable = this.select(
state => state.currentUser.currentDocumentId
);

// For simplification, I set so the clicked document is always going from 0 to 3, so switcher is serving that purpose as index

switcher = 0;

selectedDocument$: Observable = this.currentDocumentId$.pipe(
combineLatestWith(this.currentPageIndex$, this.userLogs$),
map(([docId, cpi, ls]) => {
return ls[this.switcher++ % 3];
})
);

App component observes the currently selected document's id, so in app.component.ts I have this:

currentDocumentId$ = this.store.currentDocumentId$;

In the template (app.component.html) I have a child component (EvahubSidenavComponent) that the currentDocumentId$ is propagated to simply as input, so like this:


Router will in turn contain the 2nd component (EvahubDocumentsComponent), which uses selectedDocument$ from the store like this in its template EvahubDocumentsComponent.html


{{ asyncData.selectedDocument.getDocumentContent() }}

and takes it like this, in the component itself:

selectedDocument$ = this.store.selectedDocument$;

To recap, the derived observable selectedDocument$ is somehow stopping its factor observable, currentDocumentId$, until the derived observable finishes and returns. This is not good; I want them to be processed separately and independently. I tried this for the derived observable, with the same outcome, i.e. the currentDocumentId$ is delayed:

selectedDocument$: Observable = this.currentDocumentId$.pipe(
mergeMap(cdi =>
this.currentPageIndex$.pipe(
combineLatestWith(this.userLogs$),
map(uls => {
return uls[1].find(l => l.logId == cdi);
})
)
)
);

英文:

I have the page with the documents list on the left (with clickable items, each items will select a document), and the actual content of the selected document in the right side, which takes up the bigger part of the page. The problem is that when a big document is clicked, it causes the delay to display it, but more importantly, the selected menu item (from EvahubSidenavComponent) is not displayed immediately, but it seems to be waiting the document content (in EvahubDocumentsComponent) to be displayed first. Why? The document itself, can be as much as 10MBs of text, so it's ok if it needs to be read-in over a second or two, but the menu item on the left (in the evahub-sidenav) should be coloured immediately. I have these two crucial observes in the state store as so:

currentDocumentId$: Observable<number> = this.select(
    state => state.currentUser.currentDocumentId
);

// for simplification, I set so the clicked document is always going from 0 to 3, so switcher is serving that purpose as index

switcher = 0;

selectedDocument$: Observable<EvahubDocument> = this.currentDocumentId$.pipe(
    combineLatestWith(this.currentPageIndex$, this.userLogs$),
    map(([docId, cpi, ls]) => {
        return ls[this.switcher++ % 3];
    })
);

App component observes the currently selected document's id, so in app.component.ts I have this:

currentDocumentId$ = this.store.currentDocumentId$;

In the template (app.component.html) I have a child component (EvahubSidenavComponent) that the currentDocumentId$ is propagated to simply as input, so like this:

<div *ngIf="{
    currentDocumentId: currentDocumentId$ | async,
    ...
    } as asyncData"
>
    <evahub-sidenav
        ...
        [selectedDocumentId]="asyncData.currentDocumentId"
        ...
    ></evahub-sidenav>
</div>
<router-outlet></router-outlet>

Router will in turn contain the 2nd component (EvahubDocumentsComponent), which uses selectedDocument$ from the store like this in its template EvahubDocumentsComponent.html

<div
    *ngIf="{
        selectedDocument: selectedDocument$ | async,
        currentPageIndex: currentPageIndex$ | async
    } as asyncData"
>
{{ asyncData.selectedDocument.getDocumentContent() }}

and takes it like this, in the component itself:

selectedDocument$ = this.store.selectedDocument$;

To recap, the derived observable selectedDocument$ is somehow stopping its factor observable, currentDocumentId$, until the derived observable finishes and returns. This is not good, I want them to be processed separately and independently. I tried this for the derived observable, with same outcome, i.e. the currentDocumentId$ is delayed:

selectedDocument$: Observable<any> = this.currentDocumentId$.pipe(
        mergeMap(cdi =>
            this.currentPageIndex$.pipe(
                combineLatestWith(this.userLogs$),
                map(uls => {
                    return uls[1].find(l => l.logId == cdi);
                })
            )
        )
    );

答案1

得分: 1

My guess is that you are getting information via python as a (from JavaScript's perspective) synchronous operation.

My second guess is that, because selectedDocument$ isn't using the share() operator, each observer is incrementing this.switcher and that is (or might soon be) messing things up.

Both can be resolved by changing trying

selectedDocument$: Observable<EvahubDocument> = this.currentDocumentId$.pipe(
    combineLatestWith(this.currentPageIndex$, this.userLogs$),
    map(([docId, cpi, ls]) => {
        return ls[this.switcher++ % 3];
    }),
    shareReplay(1),
    observeOn(asyncScheduler)
);

shareReplay ensures that late observers start off with the most-recent value that was emitted. Also, without it, the observable and the operators execute per observer.

observeOn emits the value on an asynchronous scheduler.

英文:

My guess is that you are getting information via python as a (from JavaScript's perspective) synchronous operation.

My second guess is that, because selectedDocument$ isn't using the share() operator, each observer is incrementing this.switcher and that is (or might soon be) messing things up.

Both can be resolved by changing trying

selectedDocument$: Observable&lt;EvahubDocument&gt; = this.currentDocumentId$.pipe(
    combineLatestWith(this.currentPageIndex$, this.userLogs$),
    map(([docId, cpi, ls]) =&gt; {
        return ls[this.switcher++ % 3];
    }),
    shareReplay(1),
    observeOn(asyncScheduler)
);

shareReplay ensures that late observers start off with the most-recent value that was emitted. Also, without it, the observable and the operators execute per observer.

observeOn emits the value on an asynchronous scheduler.

huangapple
  • 本文由 发表于 2023年5月7日 22:06:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76194424.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定