英文:
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
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
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
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<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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
- angular
- javascript
- rxjs
- rxjs-observables


评论